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> 316a2968aaSIlan Elias #include <linux/types.h> 326a2968aaSIlan Elias #include <linux/workqueue.h> 336a2968aaSIlan Elias #include <linux/completion.h> 34bc3b2d7fSPaul Gortmaker #include <linux/export.h> 356a2968aaSIlan Elias #include <linux/sched.h> 366a2968aaSIlan Elias #include <linux/bitops.h> 376a2968aaSIlan Elias #include <linux/skbuff.h> 386a2968aaSIlan Elias 396a2968aaSIlan Elias #include "../nfc.h" 406a2968aaSIlan Elias #include <net/nfc/nci.h> 416a2968aaSIlan Elias #include <net/nfc/nci_core.h> 426a2968aaSIlan Elias #include <linux/nfc.h> 436a2968aaSIlan Elias 446a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work); 456a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work); 466a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work); 476a2968aaSIlan Elias 486a2968aaSIlan Elias /* ---- NCI requests ---- */ 496a2968aaSIlan Elias 506a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result) 516a2968aaSIlan Elias { 526a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 536a2968aaSIlan Elias ndev->req_result = result; 546a2968aaSIlan Elias ndev->req_status = NCI_REQ_DONE; 556a2968aaSIlan Elias complete(&ndev->req_completion); 566a2968aaSIlan Elias } 576a2968aaSIlan Elias } 586a2968aaSIlan Elias 596a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err) 606a2968aaSIlan Elias { 616a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 626a2968aaSIlan Elias ndev->req_result = err; 636a2968aaSIlan Elias ndev->req_status = NCI_REQ_CANCELED; 646a2968aaSIlan Elias complete(&ndev->req_completion); 656a2968aaSIlan Elias } 666a2968aaSIlan Elias } 676a2968aaSIlan Elias 686a2968aaSIlan Elias /* Execute request and wait for completion. */ 696a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev, 706a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 71eb9bc6e9SSamuel Ortiz unsigned long opt, __u32 timeout) 726a2968aaSIlan Elias { 736a2968aaSIlan Elias int rc = 0; 74f8c141c3SDan Carpenter long completion_rc; 756a2968aaSIlan Elias 766a2968aaSIlan Elias ndev->req_status = NCI_REQ_PEND; 776a2968aaSIlan Elias 789bec44bfSAxel Lin reinit_completion(&ndev->req_completion); 796a2968aaSIlan Elias req(ndev, opt); 80eb9bc6e9SSamuel Ortiz completion_rc = 81eb9bc6e9SSamuel Ortiz wait_for_completion_interruptible_timeout(&ndev->req_completion, 826a2968aaSIlan Elias timeout); 836a2968aaSIlan Elias 8420c239c1SJoe Perches pr_debug("wait_for_completion return %ld\n", completion_rc); 856a2968aaSIlan Elias 866a2968aaSIlan Elias if (completion_rc > 0) { 876a2968aaSIlan Elias switch (ndev->req_status) { 886a2968aaSIlan Elias case NCI_REQ_DONE: 896a2968aaSIlan Elias rc = nci_to_errno(ndev->req_result); 906a2968aaSIlan Elias break; 916a2968aaSIlan Elias 926a2968aaSIlan Elias case NCI_REQ_CANCELED: 936a2968aaSIlan Elias rc = -ndev->req_result; 946a2968aaSIlan Elias break; 956a2968aaSIlan Elias 966a2968aaSIlan Elias default: 976a2968aaSIlan Elias rc = -ETIMEDOUT; 986a2968aaSIlan Elias break; 996a2968aaSIlan Elias } 1006a2968aaSIlan Elias } else { 101ed1e0ad8SJoe Perches pr_err("wait_for_completion_interruptible_timeout failed %ld\n", 1026a2968aaSIlan Elias completion_rc); 1036a2968aaSIlan Elias 1046a2968aaSIlan Elias rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); 1056a2968aaSIlan Elias } 1066a2968aaSIlan Elias 1076a2968aaSIlan Elias ndev->req_status = ndev->req_result = 0; 1086a2968aaSIlan Elias 1096a2968aaSIlan Elias return rc; 1106a2968aaSIlan Elias } 1116a2968aaSIlan Elias 1126a2968aaSIlan Elias static inline int nci_request(struct nci_dev *ndev, 113eb9bc6e9SSamuel Ortiz void (*req)(struct nci_dev *ndev, 114eb9bc6e9SSamuel Ortiz unsigned long opt), 1156a2968aaSIlan Elias unsigned long opt, __u32 timeout) 1166a2968aaSIlan Elias { 1176a2968aaSIlan Elias int rc; 1186a2968aaSIlan Elias 1196a2968aaSIlan Elias if (!test_bit(NCI_UP, &ndev->flags)) 1206a2968aaSIlan Elias return -ENETDOWN; 1216a2968aaSIlan Elias 1226a2968aaSIlan Elias /* Serialize all requests */ 1236a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 1246a2968aaSIlan Elias rc = __nci_request(ndev, req, opt, timeout); 1256a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 1266a2968aaSIlan Elias 1276a2968aaSIlan Elias return rc; 1286a2968aaSIlan Elias } 1296a2968aaSIlan Elias 1306a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1316a2968aaSIlan Elias { 132e8c0dacdSIlan Elias struct nci_core_reset_cmd cmd; 133e8c0dacdSIlan Elias 134e8c0dacdSIlan Elias cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 135e8c0dacdSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1366a2968aaSIlan Elias } 1376a2968aaSIlan Elias 1386a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1396a2968aaSIlan Elias { 1406a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1416a2968aaSIlan Elias } 1426a2968aaSIlan Elias 1436a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1446a2968aaSIlan Elias { 1452eb1dc10SIlan Elias struct nci_rf_disc_map_cmd cmd; 1462eb1dc10SIlan Elias struct disc_map_config *cfg = cmd.mapping_configs; 1472eb1dc10SIlan Elias __u8 *num = &cmd.num_mapping_configs; 1486a2968aaSIlan Elias int i; 1496a2968aaSIlan Elias 1506a2968aaSIlan Elias /* set rf mapping configurations */ 1512eb1dc10SIlan Elias *num = 0; 1526a2968aaSIlan Elias 1536a2968aaSIlan Elias /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1546a2968aaSIlan Elias for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1556a2968aaSIlan Elias if (ndev->supported_rf_interfaces[i] == 1566a2968aaSIlan Elias NCI_RF_INTERFACE_ISO_DEP) { 1572eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 158637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 159637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 160637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; 1612eb1dc10SIlan Elias (*num)++; 1626a2968aaSIlan Elias } else if (ndev->supported_rf_interfaces[i] == 1636a2968aaSIlan Elias NCI_RF_INTERFACE_NFC_DEP) { 1642eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 165637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 166637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 167637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; 1682eb1dc10SIlan Elias (*num)++; 1696a2968aaSIlan Elias } 1706a2968aaSIlan Elias 1712eb1dc10SIlan Elias if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 1726a2968aaSIlan Elias break; 1736a2968aaSIlan Elias } 1746a2968aaSIlan Elias 1756a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 176eb9bc6e9SSamuel Ortiz (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); 1776a2968aaSIlan Elias } 1786a2968aaSIlan Elias 1797e035230SIlan Elias struct nci_set_config_param { 1807e035230SIlan Elias __u8 id; 1817e035230SIlan Elias size_t len; 1827e035230SIlan Elias __u8 *val; 1837e035230SIlan Elias }; 1847e035230SIlan Elias 1857e035230SIlan Elias static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) 1867e035230SIlan Elias { 1877e035230SIlan Elias struct nci_set_config_param *param = (struct nci_set_config_param *)opt; 1887e035230SIlan Elias struct nci_core_set_config_cmd cmd; 1897e035230SIlan Elias 1907e035230SIlan Elias BUG_ON(param->len > NCI_MAX_PARAM_LEN); 1917e035230SIlan Elias 1927e035230SIlan Elias cmd.num_params = 1; 1937e035230SIlan Elias cmd.param.id = param->id; 1947e035230SIlan Elias cmd.param.len = param->len; 1957e035230SIlan Elias memcpy(cmd.param.val, param->val, param->len); 1967e035230SIlan Elias 1977e035230SIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); 1987e035230SIlan Elias } 1997e035230SIlan Elias 200772dccf4SJulien Lefrique struct nci_rf_discover_param { 201772dccf4SJulien Lefrique __u32 im_protocols; 202772dccf4SJulien Lefrique __u32 tm_protocols; 203772dccf4SJulien Lefrique }; 204772dccf4SJulien Lefrique 2056a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 2066a2968aaSIlan Elias { 207772dccf4SJulien Lefrique struct nci_rf_discover_param *param = 208772dccf4SJulien Lefrique (struct nci_rf_discover_param *)opt; 2096a2968aaSIlan Elias struct nci_rf_disc_cmd cmd; 2106a2968aaSIlan Elias 2116a2968aaSIlan Elias cmd.num_disc_configs = 0; 2126a2968aaSIlan Elias 2136a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 214772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_JEWEL_MASK || 215772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_MIFARE_MASK || 216772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_ISO14443_MASK || 217772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 218637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 219637d85a7SIlan Elias NCI_NFC_A_PASSIVE_POLL_MODE; 2206a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2216a2968aaSIlan Elias cmd.num_disc_configs++; 2226a2968aaSIlan Elias } 2236a2968aaSIlan Elias 2246a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 225772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { 226637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 227637d85a7SIlan Elias NCI_NFC_B_PASSIVE_POLL_MODE; 2286a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2296a2968aaSIlan Elias cmd.num_disc_configs++; 2306a2968aaSIlan Elias } 2316a2968aaSIlan Elias 2326a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 233772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_FELICA_MASK || 234772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 235637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 236637d85a7SIlan Elias NCI_NFC_F_PASSIVE_POLL_MODE; 2376a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2386a2968aaSIlan Elias cmd.num_disc_configs++; 2396a2968aaSIlan Elias } 2406a2968aaSIlan Elias 241cfdbeeafSVincent Cuissard if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 242772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { 243cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 244cfdbeeafSVincent Cuissard NCI_NFC_V_PASSIVE_POLL_MODE; 245cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 246cfdbeeafSVincent Cuissard cmd.num_disc_configs++; 247cfdbeeafSVincent Cuissard } 248cfdbeeafSVincent Cuissard 249772dccf4SJulien Lefrique if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && 250772dccf4SJulien Lefrique (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { 251772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 252772dccf4SJulien Lefrique NCI_NFC_A_PASSIVE_LISTEN_MODE; 253772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 254772dccf4SJulien Lefrique cmd.num_disc_configs++; 255772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 256772dccf4SJulien Lefrique NCI_NFC_F_PASSIVE_LISTEN_MODE; 257772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 258772dccf4SJulien Lefrique cmd.num_disc_configs++; 259772dccf4SJulien Lefrique } 260772dccf4SJulien Lefrique 2616a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 2626a2968aaSIlan Elias (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), 2636a2968aaSIlan Elias &cmd); 2646a2968aaSIlan Elias } 2656a2968aaSIlan Elias 266019c4fbaSIlan Elias struct nci_rf_discover_select_param { 267019c4fbaSIlan Elias __u8 rf_discovery_id; 268019c4fbaSIlan Elias __u8 rf_protocol; 269019c4fbaSIlan Elias }; 270019c4fbaSIlan Elias 271019c4fbaSIlan Elias static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) 272019c4fbaSIlan Elias { 273019c4fbaSIlan Elias struct nci_rf_discover_select_param *param = 274019c4fbaSIlan Elias (struct nci_rf_discover_select_param *)opt; 275019c4fbaSIlan Elias struct nci_rf_discover_select_cmd cmd; 276019c4fbaSIlan Elias 277019c4fbaSIlan Elias cmd.rf_discovery_id = param->rf_discovery_id; 278019c4fbaSIlan Elias cmd.rf_protocol = param->rf_protocol; 279019c4fbaSIlan Elias 280019c4fbaSIlan Elias switch (cmd.rf_protocol) { 281019c4fbaSIlan Elias case NCI_RF_PROTOCOL_ISO_DEP: 282019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; 283019c4fbaSIlan Elias break; 284019c4fbaSIlan Elias 285019c4fbaSIlan Elias case NCI_RF_PROTOCOL_NFC_DEP: 286019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; 287019c4fbaSIlan Elias break; 288019c4fbaSIlan Elias 289019c4fbaSIlan Elias default: 290019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_FRAME; 291019c4fbaSIlan Elias break; 292019c4fbaSIlan Elias } 293019c4fbaSIlan Elias 294019c4fbaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, 295eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_discover_select_cmd), &cmd); 296019c4fbaSIlan Elias } 297019c4fbaSIlan Elias 2986a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 2996a2968aaSIlan Elias { 3006a2968aaSIlan Elias struct nci_rf_deactivate_cmd cmd; 3016a2968aaSIlan Elias 3026a2968aaSIlan Elias cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; 3036a2968aaSIlan Elias 3046a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 305eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_deactivate_cmd), &cmd); 3066a2968aaSIlan Elias } 3076a2968aaSIlan Elias 3086a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev) 3096a2968aaSIlan Elias { 3106a2968aaSIlan Elias int rc = 0; 3116a2968aaSIlan Elias 3126a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 3136a2968aaSIlan Elias 3146a2968aaSIlan Elias if (test_bit(NCI_UP, &ndev->flags)) { 3156a2968aaSIlan Elias rc = -EALREADY; 3166a2968aaSIlan Elias goto done; 3176a2968aaSIlan Elias } 3186a2968aaSIlan Elias 3196a2968aaSIlan Elias if (ndev->ops->open(ndev)) { 3206a2968aaSIlan Elias rc = -EIO; 3216a2968aaSIlan Elias goto done; 3226a2968aaSIlan Elias } 3236a2968aaSIlan Elias 3246a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3256a2968aaSIlan Elias 3266a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 3276a2968aaSIlan Elias 3286a2968aaSIlan Elias rc = __nci_request(ndev, nci_reset_req, 0, 3296a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 3306a2968aaSIlan Elias 33144a589caSAmitkumar Karwar if (ndev->ops->setup) 33286e8586eSAmitkumar Karwar ndev->ops->setup(ndev); 33386e8586eSAmitkumar Karwar 3346a2968aaSIlan Elias if (!rc) { 3356a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_req, 0, 3366a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 3376a2968aaSIlan Elias } 3386a2968aaSIlan Elias 3396a2968aaSIlan Elias if (!rc) { 3406a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_complete_req, 0, 3416a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 3426a2968aaSIlan Elias } 3436a2968aaSIlan Elias 3446a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 3456a2968aaSIlan Elias 3466a2968aaSIlan Elias if (!rc) { 3476a2968aaSIlan Elias set_bit(NCI_UP, &ndev->flags); 348019c4fbaSIlan Elias nci_clear_target_list(ndev); 3498939e47fSIlan Elias atomic_set(&ndev->state, NCI_IDLE); 3506a2968aaSIlan Elias } else { 3516a2968aaSIlan Elias /* Init failed, cleanup */ 3526a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 3536a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 3546a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 3556a2968aaSIlan Elias 3566a2968aaSIlan Elias ndev->ops->close(ndev); 3576a2968aaSIlan Elias ndev->flags = 0; 3586a2968aaSIlan Elias } 3596a2968aaSIlan Elias 3606a2968aaSIlan Elias done: 3616a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 3626a2968aaSIlan Elias return rc; 3636a2968aaSIlan Elias } 3646a2968aaSIlan Elias 3656a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev) 3666a2968aaSIlan Elias { 3676a2968aaSIlan Elias nci_req_cancel(ndev, ENODEV); 3686a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 3696a2968aaSIlan Elias 3706a2968aaSIlan Elias if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 3716a2968aaSIlan Elias del_timer_sync(&ndev->cmd_timer); 372c4bf98b2SIlan Elias del_timer_sync(&ndev->data_timer); 3736a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 3746a2968aaSIlan Elias return 0; 3756a2968aaSIlan Elias } 3766a2968aaSIlan Elias 3776a2968aaSIlan Elias /* Drop RX and TX queues */ 3786a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 3796a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 3806a2968aaSIlan Elias 3816a2968aaSIlan Elias /* Flush RX and TX wq */ 3826a2968aaSIlan Elias flush_workqueue(ndev->rx_wq); 3836a2968aaSIlan Elias flush_workqueue(ndev->tx_wq); 3846a2968aaSIlan Elias 3856a2968aaSIlan Elias /* Reset device */ 3866a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 3876a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3886a2968aaSIlan Elias 3896a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 3906a2968aaSIlan Elias __nci_request(ndev, nci_reset_req, 0, 3916a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 3926a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 3936a2968aaSIlan Elias 394fa9be5f0SAmitkumar Karwar del_timer_sync(&ndev->cmd_timer); 395fa9be5f0SAmitkumar Karwar 3966a2968aaSIlan Elias /* Flush cmd wq */ 3976a2968aaSIlan Elias flush_workqueue(ndev->cmd_wq); 3986a2968aaSIlan Elias 3996a2968aaSIlan Elias /* After this point our queues are empty 4006a2968aaSIlan Elias * and no works are scheduled. */ 4016a2968aaSIlan Elias ndev->ops->close(ndev); 4026a2968aaSIlan Elias 4036a2968aaSIlan Elias /* Clear flags */ 4046a2968aaSIlan Elias ndev->flags = 0; 4056a2968aaSIlan Elias 4066a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 4076a2968aaSIlan Elias 4086a2968aaSIlan Elias return 0; 4096a2968aaSIlan Elias } 4106a2968aaSIlan Elias 4116a2968aaSIlan Elias /* NCI command timer function */ 4126a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg) 4136a2968aaSIlan Elias { 4146a2968aaSIlan Elias struct nci_dev *ndev = (void *) arg; 4156a2968aaSIlan Elias 4166a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4176a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 4186a2968aaSIlan Elias } 4196a2968aaSIlan Elias 420c4bf98b2SIlan Elias /* NCI data exchange timer function */ 421c4bf98b2SIlan Elias static void nci_data_timer(unsigned long arg) 422c4bf98b2SIlan Elias { 423c4bf98b2SIlan Elias struct nci_dev *ndev = (void *) arg; 424c4bf98b2SIlan Elias 425c4bf98b2SIlan Elias set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 426c4bf98b2SIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 427c4bf98b2SIlan Elias } 428c4bf98b2SIlan Elias 4296a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev) 4306a2968aaSIlan Elias { 4316a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4326a2968aaSIlan Elias 4336a2968aaSIlan Elias return nci_open_device(ndev); 4346a2968aaSIlan Elias } 4356a2968aaSIlan Elias 4366a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev) 4376a2968aaSIlan Elias { 4386a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4396a2968aaSIlan Elias 4406a2968aaSIlan Elias return nci_close_device(ndev); 4416a2968aaSIlan Elias } 4426a2968aaSIlan Elias 44322c15bf3SAmitkumar Karwar int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) 44422c15bf3SAmitkumar Karwar { 44522c15bf3SAmitkumar Karwar struct nci_set_config_param param; 44622c15bf3SAmitkumar Karwar 44722c15bf3SAmitkumar Karwar if (!val || !len) 44822c15bf3SAmitkumar Karwar return 0; 44922c15bf3SAmitkumar Karwar 45022c15bf3SAmitkumar Karwar param.id = id; 45122c15bf3SAmitkumar Karwar param.len = len; 45222c15bf3SAmitkumar Karwar param.val = val; 45322c15bf3SAmitkumar Karwar 45422c15bf3SAmitkumar Karwar return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 45522c15bf3SAmitkumar Karwar msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 45622c15bf3SAmitkumar Karwar } 45722c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config); 45822c15bf3SAmitkumar Karwar 4597e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) 4607e035230SIlan Elias { 4617e035230SIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4627e035230SIlan Elias struct nci_set_config_param param; 463529ee066SJulien Lefrique int rc; 4647e035230SIlan Elias 4657e035230SIlan Elias param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); 4667e035230SIlan Elias if ((param.val == NULL) || (param.len == 0)) 467f9fc36f4SSzymon Janc return 0; 4687e035230SIlan Elias 469460d8f97SSzymon Janc if (param.len > NFC_MAX_GT_LEN) 4707e035230SIlan Elias return -EINVAL; 4717e035230SIlan Elias 4727e035230SIlan Elias param.id = NCI_PN_ATR_REQ_GEN_BYTES; 4737e035230SIlan Elias 474529ee066SJulien Lefrique rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 475529ee066SJulien Lefrique msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 476529ee066SJulien Lefrique if (rc) 477529ee066SJulien Lefrique return rc; 478529ee066SJulien Lefrique 479529ee066SJulien Lefrique param.id = NCI_LN_ATR_RES_GEN_BYTES; 480529ee066SJulien Lefrique 481f9fc36f4SSzymon Janc return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 4827e035230SIlan Elias msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 4837e035230SIlan Elias } 4847e035230SIlan Elias 48590d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) 48690d78c13SJulien Lefrique { 48790d78c13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 48890d78c13SJulien Lefrique int rc; 48990d78c13SJulien Lefrique __u8 val; 49090d78c13SJulien Lefrique 49190d78c13SJulien Lefrique val = NCI_LA_SEL_INFO_NFC_DEP_MASK; 49290d78c13SJulien Lefrique 49390d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); 49490d78c13SJulien Lefrique if (rc) 49590d78c13SJulien Lefrique return rc; 49690d78c13SJulien Lefrique 49790d78c13SJulien Lefrique val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; 49890d78c13SJulien Lefrique 49990d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); 50090d78c13SJulien Lefrique if (rc) 50190d78c13SJulien Lefrique return rc; 50290d78c13SJulien Lefrique 50390d78c13SJulien Lefrique val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; 50490d78c13SJulien Lefrique 50590d78c13SJulien Lefrique return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); 50690d78c13SJulien Lefrique } 50790d78c13SJulien Lefrique 508fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev, 509fe7c5800SSamuel Ortiz __u32 im_protocols, __u32 tm_protocols) 5106a2968aaSIlan Elias { 5116a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 512772dccf4SJulien Lefrique struct nci_rf_discover_param param; 5136a2968aaSIlan Elias int rc; 5146a2968aaSIlan Elias 515019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || 516019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { 517ed1e0ad8SJoe Perches pr_err("unable to start poll, since poll is already active\n"); 5186a2968aaSIlan Elias return -EBUSY; 5196a2968aaSIlan Elias } 5206a2968aaSIlan Elias 521de054799SIlan Elias if (ndev->target_active_prot) { 522ed1e0ad8SJoe Perches pr_err("there is an active target\n"); 523de054799SIlan Elias return -EBUSY; 524de054799SIlan Elias } 525de054799SIlan Elias 526019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || 527019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { 528019c4fbaSIlan Elias pr_debug("target active or w4 select, implicitly deactivate\n"); 5296a2968aaSIlan Elias 5306a2968aaSIlan Elias rc = nci_request(ndev, nci_rf_deactivate_req, 0, 5316a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 5326a2968aaSIlan Elias if (rc) 5336a2968aaSIlan Elias return -EBUSY; 5346a2968aaSIlan Elias } 5356a2968aaSIlan Elias 536529ee066SJulien Lefrique if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { 5377e035230SIlan Elias rc = nci_set_local_general_bytes(nfc_dev); 5387e035230SIlan Elias if (rc) { 5397e035230SIlan Elias pr_err("failed to set local general bytes\n"); 5407e035230SIlan Elias return rc; 5417e035230SIlan Elias } 5427e035230SIlan Elias } 5437e035230SIlan Elias 54490d78c13SJulien Lefrique if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { 54590d78c13SJulien Lefrique rc = nci_set_listen_parameters(nfc_dev); 54690d78c13SJulien Lefrique if (rc) 54790d78c13SJulien Lefrique pr_err("failed to set listen parameters\n"); 54890d78c13SJulien Lefrique } 54990d78c13SJulien Lefrique 550772dccf4SJulien Lefrique param.im_protocols = im_protocols; 551772dccf4SJulien Lefrique param.tm_protocols = tm_protocols; 552772dccf4SJulien Lefrique rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, 5536a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 5546a2968aaSIlan Elias 5556a2968aaSIlan Elias if (!rc) 556fe7c5800SSamuel Ortiz ndev->poll_prots = im_protocols; 5576a2968aaSIlan Elias 5586a2968aaSIlan Elias return rc; 5596a2968aaSIlan Elias } 5606a2968aaSIlan Elias 5616a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev) 5626a2968aaSIlan Elias { 5636a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 5646a2968aaSIlan Elias 565019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && 566019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { 567ed1e0ad8SJoe Perches pr_err("unable to stop poll, since poll is not active\n"); 5686a2968aaSIlan Elias return; 5696a2968aaSIlan Elias } 5706a2968aaSIlan Elias 5716a2968aaSIlan Elias nci_request(ndev, nci_rf_deactivate_req, 0, 5726a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 5736a2968aaSIlan Elias } 5746a2968aaSIlan Elias 57590099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev, 57690099433SEric Lapuyade struct nfc_target *target, __u32 protocol) 5776a2968aaSIlan Elias { 5786a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 579019c4fbaSIlan Elias struct nci_rf_discover_select_param param; 58090099433SEric Lapuyade struct nfc_target *nci_target = NULL; 581019c4fbaSIlan Elias int i; 582019c4fbaSIlan Elias int rc = 0; 5836a2968aaSIlan Elias 58490099433SEric Lapuyade pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); 5856a2968aaSIlan Elias 586019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && 587019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 588ed1e0ad8SJoe Perches pr_err("there is no available target to activate\n"); 5896a2968aaSIlan Elias return -EINVAL; 5906a2968aaSIlan Elias } 5916a2968aaSIlan Elias 5926a2968aaSIlan Elias if (ndev->target_active_prot) { 593ed1e0ad8SJoe Perches pr_err("there is already an active target\n"); 5946a2968aaSIlan Elias return -EBUSY; 5956a2968aaSIlan Elias } 5966a2968aaSIlan Elias 597019c4fbaSIlan Elias for (i = 0; i < ndev->n_targets; i++) { 59890099433SEric Lapuyade if (ndev->targets[i].idx == target->idx) { 59990099433SEric Lapuyade nci_target = &ndev->targets[i]; 600019c4fbaSIlan Elias break; 601019c4fbaSIlan Elias } 602019c4fbaSIlan Elias } 603019c4fbaSIlan Elias 60490099433SEric Lapuyade if (!nci_target) { 605019c4fbaSIlan Elias pr_err("unable to find the selected target\n"); 606019c4fbaSIlan Elias return -EINVAL; 607019c4fbaSIlan Elias } 608019c4fbaSIlan Elias 60990099433SEric Lapuyade if (!(nci_target->supported_protocols & (1 << protocol))) { 610ed1e0ad8SJoe Perches pr_err("target does not support the requested protocol 0x%x\n", 6116a2968aaSIlan Elias protocol); 6126a2968aaSIlan Elias return -EINVAL; 6136a2968aaSIlan Elias } 6146a2968aaSIlan Elias 615019c4fbaSIlan Elias if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { 61690099433SEric Lapuyade param.rf_discovery_id = nci_target->logical_idx; 6176a2968aaSIlan Elias 618019c4fbaSIlan Elias if (protocol == NFC_PROTO_JEWEL) 619019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T1T; 620019c4fbaSIlan Elias else if (protocol == NFC_PROTO_MIFARE) 621019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T2T; 622019c4fbaSIlan Elias else if (protocol == NFC_PROTO_FELICA) 623019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T3T; 62401d719a2SSamuel Ortiz else if (protocol == NFC_PROTO_ISO14443 || 62501d719a2SSamuel Ortiz protocol == NFC_PROTO_ISO14443_B) 626019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 627019c4fbaSIlan Elias else 628019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 629019c4fbaSIlan Elias 630019c4fbaSIlan Elias rc = nci_request(ndev, nci_rf_discover_select_req, 631019c4fbaSIlan Elias (unsigned long)¶m, 632019c4fbaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); 633019c4fbaSIlan Elias } 634019c4fbaSIlan Elias 635019c4fbaSIlan Elias if (!rc) 636019c4fbaSIlan Elias ndev->target_active_prot = protocol; 637019c4fbaSIlan Elias 638019c4fbaSIlan Elias return rc; 6396a2968aaSIlan Elias } 6406a2968aaSIlan Elias 64190099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev, 64290099433SEric Lapuyade struct nfc_target *target) 6436a2968aaSIlan Elias { 6446a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6456a2968aaSIlan Elias 646767f19aeSIlan Elias pr_debug("entry\n"); 6476a2968aaSIlan Elias 6486a2968aaSIlan Elias if (!ndev->target_active_prot) { 649ed1e0ad8SJoe Perches pr_err("unable to deactivate target, no active target\n"); 6506a2968aaSIlan Elias return; 6516a2968aaSIlan Elias } 6526a2968aaSIlan Elias 6536a2968aaSIlan Elias ndev->target_active_prot = 0; 6546a2968aaSIlan Elias 6558939e47fSIlan Elias if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { 6566a2968aaSIlan Elias nci_request(ndev, nci_rf_deactivate_req, 0, 6576a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 6586a2968aaSIlan Elias } 6596a2968aaSIlan Elias } 6606a2968aaSIlan Elias 661767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 662767f19aeSIlan Elias __u8 comm_mode, __u8 *gb, size_t gb_len) 663767f19aeSIlan Elias { 664767f19aeSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 665767f19aeSIlan Elias int rc; 666767f19aeSIlan Elias 667767f19aeSIlan Elias pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); 668767f19aeSIlan Elias 669767f19aeSIlan Elias rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); 670767f19aeSIlan Elias if (rc) 671767f19aeSIlan Elias return rc; 672767f19aeSIlan Elias 673767f19aeSIlan Elias rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, 674767f19aeSIlan Elias ndev->remote_gb_len); 675767f19aeSIlan Elias if (!rc) 676767f19aeSIlan Elias rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, 677767f19aeSIlan Elias NFC_RF_INITIATOR); 678767f19aeSIlan Elias 679767f19aeSIlan Elias return rc; 680767f19aeSIlan Elias } 681767f19aeSIlan Elias 682767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev) 683767f19aeSIlan Elias { 684767f19aeSIlan Elias pr_debug("entry\n"); 685767f19aeSIlan Elias 686767f19aeSIlan Elias nci_deactivate_target(nfc_dev, NULL); 687767f19aeSIlan Elias 688767f19aeSIlan Elias return 0; 689767f19aeSIlan Elias } 690767f19aeSIlan Elias 691767f19aeSIlan Elias 692be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 6936a2968aaSIlan Elias struct sk_buff *skb, 694eb9bc6e9SSamuel Ortiz data_exchange_cb_t cb, void *cb_context) 6956a2968aaSIlan Elias { 6966a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 69738f04c6bSIlan Elias int rc; 6986a2968aaSIlan Elias 69990099433SEric Lapuyade pr_debug("target_idx %d, len %d\n", target->idx, skb->len); 7006a2968aaSIlan Elias 7016a2968aaSIlan Elias if (!ndev->target_active_prot) { 702ed1e0ad8SJoe Perches pr_err("unable to exchange data, no active target\n"); 7036a2968aaSIlan Elias return -EINVAL; 7046a2968aaSIlan Elias } 7056a2968aaSIlan Elias 70638f04c6bSIlan Elias if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 70738f04c6bSIlan Elias return -EBUSY; 70838f04c6bSIlan Elias 7096a2968aaSIlan Elias /* store cb and context to be used on receiving data */ 7106a2968aaSIlan Elias ndev->data_exchange_cb = cb; 7116a2968aaSIlan Elias ndev->data_exchange_cb_context = cb_context; 7126a2968aaSIlan Elias 713e8c0dacdSIlan Elias rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 71438f04c6bSIlan Elias if (rc) 71538f04c6bSIlan Elias clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 71638f04c6bSIlan Elias 71738f04c6bSIlan Elias return rc; 7186a2968aaSIlan Elias } 7196a2968aaSIlan Elias 7200a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 7210a946301SSamuel Ortiz { 7220a946301SSamuel Ortiz return 0; 7230a946301SSamuel Ortiz } 7240a946301SSamuel Ortiz 7250a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 7260a946301SSamuel Ortiz { 7270a946301SSamuel Ortiz return 0; 7280a946301SSamuel Ortiz } 7290a946301SSamuel Ortiz 7300a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev) 7310a946301SSamuel Ortiz { 7320a946301SSamuel Ortiz return 0; 7330a946301SSamuel Ortiz } 7340a946301SSamuel Ortiz 7356a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = { 7366a2968aaSIlan Elias .dev_up = nci_dev_up, 7376a2968aaSIlan Elias .dev_down = nci_dev_down, 7386a2968aaSIlan Elias .start_poll = nci_start_poll, 7396a2968aaSIlan Elias .stop_poll = nci_stop_poll, 740767f19aeSIlan Elias .dep_link_up = nci_dep_link_up, 741767f19aeSIlan Elias .dep_link_down = nci_dep_link_down, 7426a2968aaSIlan Elias .activate_target = nci_activate_target, 7436a2968aaSIlan Elias .deactivate_target = nci_deactivate_target, 744be9ae4ceSSamuel Ortiz .im_transceive = nci_transceive, 7450a946301SSamuel Ortiz .enable_se = nci_enable_se, 7460a946301SSamuel Ortiz .disable_se = nci_disable_se, 7470a946301SSamuel Ortiz .discover_se = nci_discover_se, 7486a2968aaSIlan Elias }; 7496a2968aaSIlan Elias 7506a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */ 7516a2968aaSIlan Elias 7526a2968aaSIlan Elias /** 7536a2968aaSIlan Elias * nci_allocate_device - allocate a new nci device 7546a2968aaSIlan Elias * 7556a2968aaSIlan Elias * @ops: device operations 7566a2968aaSIlan Elias * @supported_protocols: NFC protocols supported by the device 7576a2968aaSIlan Elias */ 7586a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops, 7596a2968aaSIlan Elias __u32 supported_protocols, 760eb9bc6e9SSamuel Ortiz int tx_headroom, int tx_tailroom) 7616a2968aaSIlan Elias { 7628ebafde0SDan Carpenter struct nci_dev *ndev; 7636a2968aaSIlan Elias 76424bf3304SJoe Perches pr_debug("supported_protocols 0x%x\n", supported_protocols); 7656a2968aaSIlan Elias 7666a2968aaSIlan Elias if (!ops->open || !ops->close || !ops->send) 7678ebafde0SDan Carpenter return NULL; 7686a2968aaSIlan Elias 7696a2968aaSIlan Elias if (!supported_protocols) 7708ebafde0SDan Carpenter return NULL; 7716a2968aaSIlan Elias 7726a2968aaSIlan Elias ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 7736a2968aaSIlan Elias if (!ndev) 7748ebafde0SDan Carpenter return NULL; 7756a2968aaSIlan Elias 7766a2968aaSIlan Elias ndev->ops = ops; 7776a2968aaSIlan Elias ndev->tx_headroom = tx_headroom; 7786a2968aaSIlan Elias ndev->tx_tailroom = tx_tailroom; 7799bec44bfSAxel Lin init_completion(&ndev->req_completion); 7806a2968aaSIlan Elias 7816a2968aaSIlan Elias ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 7826a2968aaSIlan Elias supported_protocols, 7836a2968aaSIlan Elias tx_headroom + NCI_DATA_HDR_SIZE, 7846a2968aaSIlan Elias tx_tailroom); 7856a2968aaSIlan Elias if (!ndev->nfc_dev) 7866a2968aaSIlan Elias goto free_exit; 7876a2968aaSIlan Elias 7886a2968aaSIlan Elias nfc_set_drvdata(ndev->nfc_dev, ndev); 7896a2968aaSIlan Elias 7908ebafde0SDan Carpenter return ndev; 7916a2968aaSIlan Elias 7926a2968aaSIlan Elias free_exit: 7936a2968aaSIlan Elias kfree(ndev); 7948ebafde0SDan Carpenter return NULL; 7956a2968aaSIlan Elias } 7966a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device); 7976a2968aaSIlan Elias 7986a2968aaSIlan Elias /** 7996a2968aaSIlan Elias * nci_free_device - deallocate nci device 8006a2968aaSIlan Elias * 8016a2968aaSIlan Elias * @ndev: The nci device to deallocate 8026a2968aaSIlan Elias */ 8036a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev) 8046a2968aaSIlan Elias { 8056a2968aaSIlan Elias nfc_free_device(ndev->nfc_dev); 8066a2968aaSIlan Elias kfree(ndev); 8076a2968aaSIlan Elias } 8086a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device); 8096a2968aaSIlan Elias 8106a2968aaSIlan Elias /** 8116a2968aaSIlan Elias * nci_register_device - register a nci device in the nfc subsystem 8126a2968aaSIlan Elias * 8136a2968aaSIlan Elias * @dev: The nci device to register 8146a2968aaSIlan Elias */ 8156a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev) 8166a2968aaSIlan Elias { 8176a2968aaSIlan Elias int rc; 8186a2968aaSIlan Elias struct device *dev = &ndev->nfc_dev->dev; 8196a2968aaSIlan Elias char name[32]; 8206a2968aaSIlan Elias 8216a2968aaSIlan Elias ndev->flags = 0; 8226a2968aaSIlan Elias 8236a2968aaSIlan Elias INIT_WORK(&ndev->cmd_work, nci_cmd_work); 8246a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 8256a2968aaSIlan Elias ndev->cmd_wq = create_singlethread_workqueue(name); 8266a2968aaSIlan Elias if (!ndev->cmd_wq) { 8276a2968aaSIlan Elias rc = -ENOMEM; 8283c1c0f5dSVincent Cuissard goto exit; 8296a2968aaSIlan Elias } 8306a2968aaSIlan Elias 8316a2968aaSIlan Elias INIT_WORK(&ndev->rx_work, nci_rx_work); 8326a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 8336a2968aaSIlan Elias ndev->rx_wq = create_singlethread_workqueue(name); 8346a2968aaSIlan Elias if (!ndev->rx_wq) { 8356a2968aaSIlan Elias rc = -ENOMEM; 8366a2968aaSIlan Elias goto destroy_cmd_wq_exit; 8376a2968aaSIlan Elias } 8386a2968aaSIlan Elias 8396a2968aaSIlan Elias INIT_WORK(&ndev->tx_work, nci_tx_work); 8406a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 8416a2968aaSIlan Elias ndev->tx_wq = create_singlethread_workqueue(name); 8426a2968aaSIlan Elias if (!ndev->tx_wq) { 8436a2968aaSIlan Elias rc = -ENOMEM; 8446a2968aaSIlan Elias goto destroy_rx_wq_exit; 8456a2968aaSIlan Elias } 8466a2968aaSIlan Elias 8476a2968aaSIlan Elias skb_queue_head_init(&ndev->cmd_q); 8486a2968aaSIlan Elias skb_queue_head_init(&ndev->rx_q); 8496a2968aaSIlan Elias skb_queue_head_init(&ndev->tx_q); 8506a2968aaSIlan Elias 8516a2968aaSIlan Elias setup_timer(&ndev->cmd_timer, nci_cmd_timer, 8526a2968aaSIlan Elias (unsigned long) ndev); 853c4bf98b2SIlan Elias setup_timer(&ndev->data_timer, nci_data_timer, 854c4bf98b2SIlan Elias (unsigned long) ndev); 8556a2968aaSIlan Elias 8566a2968aaSIlan Elias mutex_init(&ndev->req_lock); 8576a2968aaSIlan Elias 8583c1c0f5dSVincent Cuissard rc = nfc_register_device(ndev->nfc_dev); 8593c1c0f5dSVincent Cuissard if (rc) 8603c1c0f5dSVincent Cuissard goto destroy_rx_wq_exit; 8613c1c0f5dSVincent Cuissard 8626a2968aaSIlan Elias goto exit; 8636a2968aaSIlan Elias 8646a2968aaSIlan Elias destroy_rx_wq_exit: 8656a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 8666a2968aaSIlan Elias 8676a2968aaSIlan Elias destroy_cmd_wq_exit: 8686a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 8696a2968aaSIlan Elias 8706a2968aaSIlan Elias exit: 8716a2968aaSIlan Elias return rc; 8726a2968aaSIlan Elias } 8736a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device); 8746a2968aaSIlan Elias 8756a2968aaSIlan Elias /** 8766a2968aaSIlan Elias * nci_unregister_device - unregister a nci device in the nfc subsystem 8776a2968aaSIlan Elias * 8786a2968aaSIlan Elias * @dev: The nci device to unregister 8796a2968aaSIlan Elias */ 8806a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev) 8816a2968aaSIlan Elias { 8826a2968aaSIlan Elias nci_close_device(ndev); 8836a2968aaSIlan Elias 8846a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 8856a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 8866a2968aaSIlan Elias destroy_workqueue(ndev->tx_wq); 8876a2968aaSIlan Elias 8886a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 8896a2968aaSIlan Elias } 8906a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device); 8916a2968aaSIlan Elias 8926a2968aaSIlan Elias /** 8936a2968aaSIlan Elias * nci_recv_frame - receive frame from NCI drivers 8946a2968aaSIlan Elias * 8951095e69fSFrederic Danis * @ndev: The nci device 8966a2968aaSIlan Elias * @skb: The sk_buff to receive 8976a2968aaSIlan Elias */ 8981095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 8996a2968aaSIlan Elias { 90024bf3304SJoe Perches pr_debug("len %d\n", skb->len); 9016a2968aaSIlan Elias 902874934f4SSzymon Janc if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && 903874934f4SSzymon Janc !test_bit(NCI_INIT, &ndev->flags))) { 9046a2968aaSIlan Elias kfree_skb(skb); 9056a2968aaSIlan Elias return -ENXIO; 9066a2968aaSIlan Elias } 9076a2968aaSIlan Elias 9086a2968aaSIlan Elias /* Queue frame for rx worker thread */ 9096a2968aaSIlan Elias skb_queue_tail(&ndev->rx_q, skb); 9106a2968aaSIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 9116a2968aaSIlan Elias 9126a2968aaSIlan Elias return 0; 9136a2968aaSIlan Elias } 9146a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame); 9156a2968aaSIlan Elias 9161095e69fSFrederic Danis static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) 9176a2968aaSIlan Elias { 91824bf3304SJoe Perches pr_debug("len %d\n", skb->len); 9196a2968aaSIlan Elias 9206a2968aaSIlan Elias if (!ndev) { 9216a2968aaSIlan Elias kfree_skb(skb); 9226a2968aaSIlan Elias return -ENODEV; 9236a2968aaSIlan Elias } 9246a2968aaSIlan Elias 9256a2968aaSIlan Elias /* Get rid of skb owner, prior to sending to the driver. */ 9266a2968aaSIlan Elias skb_orphan(skb); 9276a2968aaSIlan Elias 92805158296SHiren Tandel /* Send copy to sniffer */ 92905158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 93005158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); 93105158296SHiren Tandel 9321095e69fSFrederic Danis return ndev->ops->send(ndev, skb); 9336a2968aaSIlan Elias } 9346a2968aaSIlan Elias 9356a2968aaSIlan Elias /* Send NCI command */ 9366a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 9376a2968aaSIlan Elias { 9386a2968aaSIlan Elias struct nci_ctrl_hdr *hdr; 9396a2968aaSIlan Elias struct sk_buff *skb; 9406a2968aaSIlan Elias 94124bf3304SJoe Perches pr_debug("opcode 0x%x, plen %d\n", opcode, plen); 9426a2968aaSIlan Elias 9436a2968aaSIlan Elias skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 9446a2968aaSIlan Elias if (!skb) { 945ed1e0ad8SJoe Perches pr_err("no memory for command\n"); 9466a2968aaSIlan Elias return -ENOMEM; 9476a2968aaSIlan Elias } 9486a2968aaSIlan Elias 9496a2968aaSIlan Elias hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); 9506a2968aaSIlan Elias hdr->gid = nci_opcode_gid(opcode); 9516a2968aaSIlan Elias hdr->oid = nci_opcode_oid(opcode); 9526a2968aaSIlan Elias hdr->plen = plen; 9536a2968aaSIlan Elias 9546a2968aaSIlan Elias nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 9556a2968aaSIlan Elias nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 9566a2968aaSIlan Elias 9576a2968aaSIlan Elias if (plen) 9586a2968aaSIlan Elias memcpy(skb_put(skb, plen), payload, plen); 9596a2968aaSIlan Elias 9606a2968aaSIlan Elias skb_queue_tail(&ndev->cmd_q, skb); 9616a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 9626a2968aaSIlan Elias 9636a2968aaSIlan Elias return 0; 9646a2968aaSIlan Elias } 9656a2968aaSIlan Elias 9666a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */ 9676a2968aaSIlan Elias 9686a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work) 9696a2968aaSIlan Elias { 9706a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 9716a2968aaSIlan Elias struct sk_buff *skb; 9726a2968aaSIlan Elias 97324bf3304SJoe Perches pr_debug("credits_cnt %d\n", atomic_read(&ndev->credits_cnt)); 9746a2968aaSIlan Elias 9756a2968aaSIlan Elias /* Send queued tx data */ 9766a2968aaSIlan Elias while (atomic_read(&ndev->credits_cnt)) { 9776a2968aaSIlan Elias skb = skb_dequeue(&ndev->tx_q); 9786a2968aaSIlan Elias if (!skb) 9796a2968aaSIlan Elias return; 9806a2968aaSIlan Elias 981db98c829SIlan Elias /* Check if data flow control is used */ 982db98c829SIlan Elias if (atomic_read(&ndev->credits_cnt) != 983db98c829SIlan Elias NCI_DATA_FLOW_CONTROL_NOT_USED) 9846a2968aaSIlan Elias atomic_dec(&ndev->credits_cnt); 9856a2968aaSIlan Elias 98620c239c1SJoe Perches pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 9876a2968aaSIlan Elias nci_pbf(skb->data), 9886a2968aaSIlan Elias nci_conn_id(skb->data), 9896a2968aaSIlan Elias nci_plen(skb->data)); 9906a2968aaSIlan Elias 9911095e69fSFrederic Danis nci_send_frame(ndev, skb); 992c4bf98b2SIlan Elias 993c4bf98b2SIlan Elias mod_timer(&ndev->data_timer, 994c4bf98b2SIlan Elias jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); 9956a2968aaSIlan Elias } 9966a2968aaSIlan Elias } 9976a2968aaSIlan Elias 9986a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */ 9996a2968aaSIlan Elias 10006a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work) 10016a2968aaSIlan Elias { 10026a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 10036a2968aaSIlan Elias struct sk_buff *skb; 10046a2968aaSIlan Elias 10056a2968aaSIlan Elias while ((skb = skb_dequeue(&ndev->rx_q))) { 100605158296SHiren Tandel 100705158296SHiren Tandel /* Send copy to sniffer */ 100805158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 100905158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 101005158296SHiren Tandel 10116a2968aaSIlan Elias /* Process frame */ 10126a2968aaSIlan Elias switch (nci_mt(skb->data)) { 10136a2968aaSIlan Elias case NCI_MT_RSP_PKT: 10146a2968aaSIlan Elias nci_rsp_packet(ndev, skb); 10156a2968aaSIlan Elias break; 10166a2968aaSIlan Elias 10176a2968aaSIlan Elias case NCI_MT_NTF_PKT: 10186a2968aaSIlan Elias nci_ntf_packet(ndev, skb); 10196a2968aaSIlan Elias break; 10206a2968aaSIlan Elias 10216a2968aaSIlan Elias case NCI_MT_DATA_PKT: 10226a2968aaSIlan Elias nci_rx_data_packet(ndev, skb); 10236a2968aaSIlan Elias break; 10246a2968aaSIlan Elias 10256a2968aaSIlan Elias default: 1026ed1e0ad8SJoe Perches pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 10276a2968aaSIlan Elias kfree_skb(skb); 10286a2968aaSIlan Elias break; 10296a2968aaSIlan Elias } 10306a2968aaSIlan Elias } 1031c4bf98b2SIlan Elias 1032c4bf98b2SIlan Elias /* check if a data exchange timout has occurred */ 1033c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { 1034c4bf98b2SIlan Elias /* complete the data exchange transaction, if exists */ 1035c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 1036c4bf98b2SIlan Elias nci_data_exchange_complete(ndev, NULL, -ETIMEDOUT); 1037c4bf98b2SIlan Elias 1038c4bf98b2SIlan Elias clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 1039c4bf98b2SIlan Elias } 10406a2968aaSIlan Elias } 10416a2968aaSIlan Elias 10426a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */ 10436a2968aaSIlan Elias 10446a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work) 10456a2968aaSIlan Elias { 10466a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 10476a2968aaSIlan Elias struct sk_buff *skb; 10486a2968aaSIlan Elias 104924bf3304SJoe Perches pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 10506a2968aaSIlan Elias 10516a2968aaSIlan Elias /* Send queued command */ 10526a2968aaSIlan Elias if (atomic_read(&ndev->cmd_cnt)) { 10536a2968aaSIlan Elias skb = skb_dequeue(&ndev->cmd_q); 10546a2968aaSIlan Elias if (!skb) 10556a2968aaSIlan Elias return; 10566a2968aaSIlan Elias 10576a2968aaSIlan Elias atomic_dec(&ndev->cmd_cnt); 10586a2968aaSIlan Elias 105920c239c1SJoe Perches pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 10606a2968aaSIlan Elias nci_pbf(skb->data), 10616a2968aaSIlan Elias nci_opcode_gid(nci_opcode(skb->data)), 10626a2968aaSIlan Elias nci_opcode_oid(nci_opcode(skb->data)), 10636a2968aaSIlan Elias nci_plen(skb->data)); 10646a2968aaSIlan Elias 10651095e69fSFrederic Danis nci_send_frame(ndev, skb); 10666a2968aaSIlan Elias 10676a2968aaSIlan Elias mod_timer(&ndev->cmd_timer, 10686a2968aaSIlan Elias jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 10696a2968aaSIlan Elias } 10706a2968aaSIlan Elias } 10718a70e7f8SDave Jones 10728a70e7f8SDave Jones MODULE_LICENSE("GPL"); 1073