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 484aeee687SChristophe Ricard struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev, 494aeee687SChristophe Ricard int conn_id) 504aeee687SChristophe Ricard { 514aeee687SChristophe Ricard struct nci_conn_info *conn_info; 524aeee687SChristophe Ricard 534aeee687SChristophe Ricard list_for_each_entry(conn_info, &ndev->conn_info_list, list) { 544aeee687SChristophe Ricard if (conn_info->conn_id == conn_id) 554aeee687SChristophe Ricard return conn_info; 564aeee687SChristophe Ricard } 574aeee687SChristophe Ricard 584aeee687SChristophe Ricard return NULL; 594aeee687SChristophe Ricard } 604aeee687SChristophe Ricard 616a2968aaSIlan Elias /* ---- NCI requests ---- */ 626a2968aaSIlan Elias 636a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result) 646a2968aaSIlan Elias { 656a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 666a2968aaSIlan Elias ndev->req_result = result; 676a2968aaSIlan Elias ndev->req_status = NCI_REQ_DONE; 686a2968aaSIlan Elias complete(&ndev->req_completion); 696a2968aaSIlan Elias } 706a2968aaSIlan Elias } 716a2968aaSIlan Elias 726a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err) 736a2968aaSIlan Elias { 746a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 756a2968aaSIlan Elias ndev->req_result = err; 766a2968aaSIlan Elias ndev->req_status = NCI_REQ_CANCELED; 776a2968aaSIlan Elias complete(&ndev->req_completion); 786a2968aaSIlan Elias } 796a2968aaSIlan Elias } 806a2968aaSIlan Elias 816a2968aaSIlan Elias /* Execute request and wait for completion. */ 826a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev, 836a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 84eb9bc6e9SSamuel Ortiz unsigned long opt, __u32 timeout) 856a2968aaSIlan Elias { 866a2968aaSIlan Elias int rc = 0; 87f8c141c3SDan Carpenter long completion_rc; 886a2968aaSIlan Elias 896a2968aaSIlan Elias ndev->req_status = NCI_REQ_PEND; 906a2968aaSIlan Elias 919bec44bfSAxel Lin reinit_completion(&ndev->req_completion); 926a2968aaSIlan Elias req(ndev, opt); 93eb9bc6e9SSamuel Ortiz completion_rc = 94eb9bc6e9SSamuel Ortiz wait_for_completion_interruptible_timeout(&ndev->req_completion, 956a2968aaSIlan Elias timeout); 966a2968aaSIlan Elias 9720c239c1SJoe Perches pr_debug("wait_for_completion return %ld\n", completion_rc); 986a2968aaSIlan Elias 996a2968aaSIlan Elias if (completion_rc > 0) { 1006a2968aaSIlan Elias switch (ndev->req_status) { 1016a2968aaSIlan Elias case NCI_REQ_DONE: 1026a2968aaSIlan Elias rc = nci_to_errno(ndev->req_result); 1036a2968aaSIlan Elias break; 1046a2968aaSIlan Elias 1056a2968aaSIlan Elias case NCI_REQ_CANCELED: 1066a2968aaSIlan Elias rc = -ndev->req_result; 1076a2968aaSIlan Elias break; 1086a2968aaSIlan Elias 1096a2968aaSIlan Elias default: 1106a2968aaSIlan Elias rc = -ETIMEDOUT; 1116a2968aaSIlan Elias break; 1126a2968aaSIlan Elias } 1136a2968aaSIlan Elias } else { 114ed1e0ad8SJoe Perches pr_err("wait_for_completion_interruptible_timeout failed %ld\n", 1156a2968aaSIlan Elias completion_rc); 1166a2968aaSIlan Elias 1176a2968aaSIlan Elias rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); 1186a2968aaSIlan Elias } 1196a2968aaSIlan Elias 1206a2968aaSIlan Elias ndev->req_status = ndev->req_result = 0; 1216a2968aaSIlan Elias 1226a2968aaSIlan Elias return rc; 1236a2968aaSIlan Elias } 1246a2968aaSIlan Elias 12511f54f22SChristophe Ricard inline int nci_request(struct nci_dev *ndev, 126eb9bc6e9SSamuel Ortiz void (*req)(struct nci_dev *ndev, 127eb9bc6e9SSamuel Ortiz unsigned long opt), 1286a2968aaSIlan Elias unsigned long opt, __u32 timeout) 1296a2968aaSIlan Elias { 1306a2968aaSIlan Elias int rc; 1316a2968aaSIlan Elias 1326a2968aaSIlan Elias if (!test_bit(NCI_UP, &ndev->flags)) 1336a2968aaSIlan Elias return -ENETDOWN; 1346a2968aaSIlan Elias 1356a2968aaSIlan Elias /* Serialize all requests */ 1366a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 1376a2968aaSIlan Elias rc = __nci_request(ndev, req, opt, timeout); 1386a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 1396a2968aaSIlan Elias 1406a2968aaSIlan Elias return rc; 1416a2968aaSIlan Elias } 1426a2968aaSIlan Elias 1436a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1446a2968aaSIlan Elias { 145e8c0dacdSIlan Elias struct nci_core_reset_cmd cmd; 146e8c0dacdSIlan Elias 147e8c0dacdSIlan Elias cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 148e8c0dacdSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1496a2968aaSIlan Elias } 1506a2968aaSIlan Elias 1516a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1526a2968aaSIlan Elias { 1536a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1546a2968aaSIlan Elias } 1556a2968aaSIlan Elias 1566a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1576a2968aaSIlan Elias { 1582eb1dc10SIlan Elias struct nci_rf_disc_map_cmd cmd; 1592eb1dc10SIlan Elias struct disc_map_config *cfg = cmd.mapping_configs; 1602eb1dc10SIlan Elias __u8 *num = &cmd.num_mapping_configs; 1616a2968aaSIlan Elias int i; 1626a2968aaSIlan Elias 1636a2968aaSIlan Elias /* set rf mapping configurations */ 1642eb1dc10SIlan Elias *num = 0; 1656a2968aaSIlan Elias 1666a2968aaSIlan Elias /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1676a2968aaSIlan Elias for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1686a2968aaSIlan Elias if (ndev->supported_rf_interfaces[i] == 1696a2968aaSIlan Elias NCI_RF_INTERFACE_ISO_DEP) { 1702eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 171637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 172637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 173637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP; 1742eb1dc10SIlan Elias (*num)++; 1756a2968aaSIlan Elias } else if (ndev->supported_rf_interfaces[i] == 1766a2968aaSIlan Elias NCI_RF_INTERFACE_NFC_DEP) { 1772eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 178637d85a7SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_POLL | 179637d85a7SIlan Elias NCI_DISC_MAP_MODE_LISTEN; 180637d85a7SIlan Elias cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP; 1812eb1dc10SIlan Elias (*num)++; 1826a2968aaSIlan Elias } 1836a2968aaSIlan Elias 1842eb1dc10SIlan Elias if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 1856a2968aaSIlan Elias break; 1866a2968aaSIlan Elias } 1876a2968aaSIlan Elias 1886a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 189eb9bc6e9SSamuel Ortiz (1 + ((*num) * sizeof(struct disc_map_config))), &cmd); 1906a2968aaSIlan Elias } 1916a2968aaSIlan Elias 1927e035230SIlan Elias struct nci_set_config_param { 1937e035230SIlan Elias __u8 id; 1947e035230SIlan Elias size_t len; 1957e035230SIlan Elias __u8 *val; 1967e035230SIlan Elias }; 1977e035230SIlan Elias 1987e035230SIlan Elias static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt) 1997e035230SIlan Elias { 2007e035230SIlan Elias struct nci_set_config_param *param = (struct nci_set_config_param *)opt; 2017e035230SIlan Elias struct nci_core_set_config_cmd cmd; 2027e035230SIlan Elias 2037e035230SIlan Elias BUG_ON(param->len > NCI_MAX_PARAM_LEN); 2047e035230SIlan Elias 2057e035230SIlan Elias cmd.num_params = 1; 2067e035230SIlan Elias cmd.param.id = param->id; 2077e035230SIlan Elias cmd.param.len = param->len; 2087e035230SIlan Elias memcpy(cmd.param.val, param->val, param->len); 2097e035230SIlan Elias 2107e035230SIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd); 2117e035230SIlan Elias } 2127e035230SIlan Elias 213772dccf4SJulien Lefrique struct nci_rf_discover_param { 214772dccf4SJulien Lefrique __u32 im_protocols; 215772dccf4SJulien Lefrique __u32 tm_protocols; 216772dccf4SJulien Lefrique }; 217772dccf4SJulien Lefrique 2186a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 2196a2968aaSIlan Elias { 220772dccf4SJulien Lefrique struct nci_rf_discover_param *param = 221772dccf4SJulien Lefrique (struct nci_rf_discover_param *)opt; 2226a2968aaSIlan Elias struct nci_rf_disc_cmd cmd; 2236a2968aaSIlan Elias 2246a2968aaSIlan Elias cmd.num_disc_configs = 0; 2256a2968aaSIlan Elias 2266a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 227772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_JEWEL_MASK || 228772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_MIFARE_MASK || 229772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_ISO14443_MASK || 230772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 231637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 232637d85a7SIlan Elias NCI_NFC_A_PASSIVE_POLL_MODE; 2336a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2346a2968aaSIlan Elias cmd.num_disc_configs++; 2356a2968aaSIlan Elias } 2366a2968aaSIlan Elias 2376a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 238772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) { 239637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 240637d85a7SIlan Elias NCI_NFC_B_PASSIVE_POLL_MODE; 2416a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2426a2968aaSIlan Elias cmd.num_disc_configs++; 2436a2968aaSIlan Elias } 2446a2968aaSIlan Elias 2456a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 246772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_FELICA_MASK || 247772dccf4SJulien Lefrique param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) { 248637d85a7SIlan Elias cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 249637d85a7SIlan Elias NCI_NFC_F_PASSIVE_POLL_MODE; 2506a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2516a2968aaSIlan Elias cmd.num_disc_configs++; 2526a2968aaSIlan Elias } 2536a2968aaSIlan Elias 254cfdbeeafSVincent Cuissard if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 255772dccf4SJulien Lefrique (param->im_protocols & NFC_PROTO_ISO15693_MASK)) { 256cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 257cfdbeeafSVincent Cuissard NCI_NFC_V_PASSIVE_POLL_MODE; 258cfdbeeafSVincent Cuissard cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 259cfdbeeafSVincent Cuissard cmd.num_disc_configs++; 260cfdbeeafSVincent Cuissard } 261cfdbeeafSVincent Cuissard 262772dccf4SJulien Lefrique if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) && 263772dccf4SJulien Lefrique (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) { 264772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 265772dccf4SJulien Lefrique NCI_NFC_A_PASSIVE_LISTEN_MODE; 266772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 267772dccf4SJulien Lefrique cmd.num_disc_configs++; 268772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode = 269772dccf4SJulien Lefrique NCI_NFC_F_PASSIVE_LISTEN_MODE; 270772dccf4SJulien Lefrique cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 271772dccf4SJulien Lefrique cmd.num_disc_configs++; 272772dccf4SJulien Lefrique } 273772dccf4SJulien Lefrique 2746a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 2756a2968aaSIlan Elias (1 + (cmd.num_disc_configs * sizeof(struct disc_config))), 2766a2968aaSIlan Elias &cmd); 2776a2968aaSIlan Elias } 2786a2968aaSIlan Elias 279019c4fbaSIlan Elias struct nci_rf_discover_select_param { 280019c4fbaSIlan Elias __u8 rf_discovery_id; 281019c4fbaSIlan Elias __u8 rf_protocol; 282019c4fbaSIlan Elias }; 283019c4fbaSIlan Elias 284019c4fbaSIlan Elias static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt) 285019c4fbaSIlan Elias { 286019c4fbaSIlan Elias struct nci_rf_discover_select_param *param = 287019c4fbaSIlan Elias (struct nci_rf_discover_select_param *)opt; 288019c4fbaSIlan Elias struct nci_rf_discover_select_cmd cmd; 289019c4fbaSIlan Elias 290019c4fbaSIlan Elias cmd.rf_discovery_id = param->rf_discovery_id; 291019c4fbaSIlan Elias cmd.rf_protocol = param->rf_protocol; 292019c4fbaSIlan Elias 293019c4fbaSIlan Elias switch (cmd.rf_protocol) { 294019c4fbaSIlan Elias case NCI_RF_PROTOCOL_ISO_DEP: 295019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP; 296019c4fbaSIlan Elias break; 297019c4fbaSIlan Elias 298019c4fbaSIlan Elias case NCI_RF_PROTOCOL_NFC_DEP: 299019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP; 300019c4fbaSIlan Elias break; 301019c4fbaSIlan Elias 302019c4fbaSIlan Elias default: 303019c4fbaSIlan Elias cmd.rf_interface = NCI_RF_INTERFACE_FRAME; 304019c4fbaSIlan Elias break; 305019c4fbaSIlan Elias } 306019c4fbaSIlan Elias 307019c4fbaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD, 308eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_discover_select_cmd), &cmd); 309019c4fbaSIlan Elias } 310019c4fbaSIlan Elias 3116a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 3126a2968aaSIlan Elias { 3136a2968aaSIlan Elias struct nci_rf_deactivate_cmd cmd; 3146a2968aaSIlan Elias 3159295b5b5SChristophe Ricard cmd.type = opt; 3166a2968aaSIlan Elias 3176a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 318eb9bc6e9SSamuel Ortiz sizeof(struct nci_rf_deactivate_cmd), &cmd); 3196a2968aaSIlan Elias } 3206a2968aaSIlan Elias 3216a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev) 3226a2968aaSIlan Elias { 3236a2968aaSIlan Elias int rc = 0; 3246a2968aaSIlan Elias 3256a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 3266a2968aaSIlan Elias 3276a2968aaSIlan Elias if (test_bit(NCI_UP, &ndev->flags)) { 3286a2968aaSIlan Elias rc = -EALREADY; 3296a2968aaSIlan Elias goto done; 3306a2968aaSIlan Elias } 3316a2968aaSIlan Elias 3326a2968aaSIlan Elias if (ndev->ops->open(ndev)) { 3336a2968aaSIlan Elias rc = -EIO; 3346a2968aaSIlan Elias goto done; 3356a2968aaSIlan Elias } 3366a2968aaSIlan Elias 3376a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3386a2968aaSIlan Elias 3396a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 3406a2968aaSIlan Elias 3416a2968aaSIlan Elias rc = __nci_request(ndev, nci_reset_req, 0, 3426a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 3436a2968aaSIlan Elias 34444a589caSAmitkumar Karwar if (ndev->ops->setup) 34586e8586eSAmitkumar Karwar ndev->ops->setup(ndev); 34686e8586eSAmitkumar Karwar 3476a2968aaSIlan Elias if (!rc) { 3486a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_req, 0, 3496a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 3506a2968aaSIlan Elias } 3516a2968aaSIlan Elias 3526a2968aaSIlan Elias if (!rc) { 3536a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_complete_req, 0, 3546a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 3556a2968aaSIlan Elias } 3566a2968aaSIlan Elias 3576a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 3586a2968aaSIlan Elias 3596a2968aaSIlan Elias if (!rc) { 3606a2968aaSIlan Elias set_bit(NCI_UP, &ndev->flags); 361019c4fbaSIlan Elias nci_clear_target_list(ndev); 3628939e47fSIlan Elias atomic_set(&ndev->state, NCI_IDLE); 3636a2968aaSIlan Elias } else { 3646a2968aaSIlan Elias /* Init failed, cleanup */ 3656a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 3666a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 3676a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 3686a2968aaSIlan Elias 3696a2968aaSIlan Elias ndev->ops->close(ndev); 3706a2968aaSIlan Elias ndev->flags = 0; 3716a2968aaSIlan Elias } 3726a2968aaSIlan Elias 3736a2968aaSIlan Elias done: 3746a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 3756a2968aaSIlan Elias return rc; 3766a2968aaSIlan Elias } 3776a2968aaSIlan Elias 3786a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev) 3796a2968aaSIlan Elias { 3806a2968aaSIlan Elias nci_req_cancel(ndev, ENODEV); 3816a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 3826a2968aaSIlan Elias 3836a2968aaSIlan Elias if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 3846a2968aaSIlan Elias del_timer_sync(&ndev->cmd_timer); 385c4bf98b2SIlan Elias del_timer_sync(&ndev->data_timer); 3866a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 3876a2968aaSIlan Elias return 0; 3886a2968aaSIlan Elias } 3896a2968aaSIlan Elias 3906a2968aaSIlan Elias /* Drop RX and TX queues */ 3916a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 3926a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 3936a2968aaSIlan Elias 3946a2968aaSIlan Elias /* Flush RX and TX wq */ 3956a2968aaSIlan Elias flush_workqueue(ndev->rx_wq); 3966a2968aaSIlan Elias flush_workqueue(ndev->tx_wq); 3976a2968aaSIlan Elias 3986a2968aaSIlan Elias /* Reset device */ 3996a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 4006a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4016a2968aaSIlan Elias 4026a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 4036a2968aaSIlan Elias __nci_request(ndev, nci_reset_req, 0, 4046a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 4056a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 4066a2968aaSIlan Elias 407fa9be5f0SAmitkumar Karwar del_timer_sync(&ndev->cmd_timer); 408fa9be5f0SAmitkumar Karwar 4096a2968aaSIlan Elias /* Flush cmd wq */ 4106a2968aaSIlan Elias flush_workqueue(ndev->cmd_wq); 4116a2968aaSIlan Elias 4126a2968aaSIlan Elias /* After this point our queues are empty 4136a2968aaSIlan Elias * and no works are scheduled. */ 4146a2968aaSIlan Elias ndev->ops->close(ndev); 4156a2968aaSIlan Elias 4166a2968aaSIlan Elias /* Clear flags */ 4176a2968aaSIlan Elias ndev->flags = 0; 4186a2968aaSIlan Elias 4196a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 4206a2968aaSIlan Elias 4216a2968aaSIlan Elias return 0; 4226a2968aaSIlan Elias } 4236a2968aaSIlan Elias 4246a2968aaSIlan Elias /* NCI command timer function */ 4256a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg) 4266a2968aaSIlan Elias { 4276a2968aaSIlan Elias struct nci_dev *ndev = (void *) arg; 4286a2968aaSIlan Elias 4296a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 4306a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 4316a2968aaSIlan Elias } 4326a2968aaSIlan Elias 433c4bf98b2SIlan Elias /* NCI data exchange timer function */ 434c4bf98b2SIlan Elias static void nci_data_timer(unsigned long arg) 435c4bf98b2SIlan Elias { 436c4bf98b2SIlan Elias struct nci_dev *ndev = (void *) arg; 437c4bf98b2SIlan Elias 438c4bf98b2SIlan Elias set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 439c4bf98b2SIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 440c4bf98b2SIlan Elias } 441c4bf98b2SIlan Elias 4426a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev) 4436a2968aaSIlan Elias { 4446a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4456a2968aaSIlan Elias 4466a2968aaSIlan Elias return nci_open_device(ndev); 4476a2968aaSIlan Elias } 4486a2968aaSIlan Elias 4496a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev) 4506a2968aaSIlan Elias { 4516a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4526a2968aaSIlan Elias 4536a2968aaSIlan Elias return nci_close_device(ndev); 4546a2968aaSIlan Elias } 4556a2968aaSIlan Elias 45622c15bf3SAmitkumar Karwar int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val) 45722c15bf3SAmitkumar Karwar { 45822c15bf3SAmitkumar Karwar struct nci_set_config_param param; 45922c15bf3SAmitkumar Karwar 46022c15bf3SAmitkumar Karwar if (!val || !len) 46122c15bf3SAmitkumar Karwar return 0; 46222c15bf3SAmitkumar Karwar 46322c15bf3SAmitkumar Karwar param.id = id; 46422c15bf3SAmitkumar Karwar param.len = len; 46522c15bf3SAmitkumar Karwar param.val = val; 46622c15bf3SAmitkumar Karwar 46722c15bf3SAmitkumar Karwar return __nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 46822c15bf3SAmitkumar Karwar msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 46922c15bf3SAmitkumar Karwar } 47022c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config); 47122c15bf3SAmitkumar Karwar 472af9c8aa6SChristophe Ricard static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt) 473af9c8aa6SChristophe Ricard { 474af9c8aa6SChristophe Ricard struct nci_nfcee_discover_cmd cmd; 475af9c8aa6SChristophe Ricard __u8 action = opt; 476af9c8aa6SChristophe Ricard 477af9c8aa6SChristophe Ricard cmd.discovery_action = action; 478af9c8aa6SChristophe Ricard 479af9c8aa6SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd); 480af9c8aa6SChristophe Ricard } 481af9c8aa6SChristophe Ricard 482af9c8aa6SChristophe Ricard int nci_nfcee_discover(struct nci_dev *ndev, u8 action) 483af9c8aa6SChristophe Ricard { 484af9c8aa6SChristophe Ricard return nci_request(ndev, nci_nfcee_discover_req, action, 485af9c8aa6SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 486af9c8aa6SChristophe Ricard } 487af9c8aa6SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_discover); 488af9c8aa6SChristophe Ricard 489f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt) 490f7f793f3SChristophe Ricard { 491f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd *cmd = 492f7f793f3SChristophe Ricard (struct nci_nfcee_mode_set_cmd *)opt; 493f7f793f3SChristophe Ricard 494f7f793f3SChristophe Ricard nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD, 495f7f793f3SChristophe Ricard sizeof(struct nci_nfcee_mode_set_cmd), cmd); 496f7f793f3SChristophe Ricard } 497f7f793f3SChristophe Ricard 498f7f793f3SChristophe Ricard int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode) 499f7f793f3SChristophe Ricard { 500f7f793f3SChristophe Ricard struct nci_nfcee_mode_set_cmd cmd; 501f7f793f3SChristophe Ricard 502f7f793f3SChristophe Ricard cmd.nfcee_id = nfcee_id; 503f7f793f3SChristophe Ricard cmd.nfcee_mode = nfcee_mode; 504f7f793f3SChristophe Ricard 505f7f793f3SChristophe Ricard return nci_request(ndev, nci_nfcee_mode_set_req, (unsigned long)&cmd, 506f7f793f3SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 507f7f793f3SChristophe Ricard } 508f7f793f3SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_mode_set); 509f7f793f3SChristophe Ricard 510736bb957SChristophe Ricard static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt) 511736bb957SChristophe Ricard { 512736bb957SChristophe Ricard struct nci_core_conn_create_cmd cmd; 513736bb957SChristophe Ricard struct core_conn_create_dest_spec_params *params = 514736bb957SChristophe Ricard (struct core_conn_create_dest_spec_params *)opt; 515736bb957SChristophe Ricard 516736bb957SChristophe Ricard cmd.destination_type = NCI_DESTINATION_NFCEE; 517736bb957SChristophe Ricard cmd.number_destination_params = 1; 518736bb957SChristophe Ricard memcpy(&cmd.params.type, params, 519736bb957SChristophe Ricard sizeof(struct core_conn_create_dest_spec_params)); 520736bb957SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, 521736bb957SChristophe Ricard sizeof(struct nci_core_conn_create_cmd), &cmd); 522736bb957SChristophe Ricard } 523736bb957SChristophe Ricard 524736bb957SChristophe Ricard int nci_core_conn_create(struct nci_dev *ndev, 525736bb957SChristophe Ricard struct core_conn_create_dest_spec_params *params) 526736bb957SChristophe Ricard { 527736bb957SChristophe Ricard ndev->cur_id = params->value.id; 528736bb957SChristophe Ricard return nci_request(ndev, nci_core_conn_create_req, 529736bb957SChristophe Ricard (unsigned long)params, 530736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 531736bb957SChristophe Ricard } 532736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_create); 533736bb957SChristophe Ricard 534736bb957SChristophe Ricard static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt) 535736bb957SChristophe Ricard { 536736bb957SChristophe Ricard __u8 conn_id = opt; 537736bb957SChristophe Ricard 538736bb957SChristophe Ricard nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id); 539736bb957SChristophe Ricard } 540736bb957SChristophe Ricard 541736bb957SChristophe Ricard int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id) 542736bb957SChristophe Ricard { 543736bb957SChristophe Ricard return nci_request(ndev, nci_core_conn_close_req, conn_id, 544736bb957SChristophe Ricard msecs_to_jiffies(NCI_CMD_TIMEOUT)); 545736bb957SChristophe Ricard } 546736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_close); 547736bb957SChristophe Ricard 5487e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev) 5497e035230SIlan Elias { 5507e035230SIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 5517e035230SIlan Elias struct nci_set_config_param param; 552529ee066SJulien Lefrique int rc; 5537e035230SIlan Elias 5547e035230SIlan Elias param.val = nfc_get_local_general_bytes(nfc_dev, ¶m.len); 5557e035230SIlan Elias if ((param.val == NULL) || (param.len == 0)) 556f9fc36f4SSzymon Janc return 0; 5577e035230SIlan Elias 558460d8f97SSzymon Janc if (param.len > NFC_MAX_GT_LEN) 5597e035230SIlan Elias return -EINVAL; 5607e035230SIlan Elias 5617e035230SIlan Elias param.id = NCI_PN_ATR_REQ_GEN_BYTES; 5627e035230SIlan Elias 563529ee066SJulien Lefrique rc = nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 564529ee066SJulien Lefrique msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 565529ee066SJulien Lefrique if (rc) 566529ee066SJulien Lefrique return rc; 567529ee066SJulien Lefrique 568529ee066SJulien Lefrique param.id = NCI_LN_ATR_RES_GEN_BYTES; 569529ee066SJulien Lefrique 570f9fc36f4SSzymon Janc return nci_request(ndev, nci_set_config_req, (unsigned long)¶m, 5717e035230SIlan Elias msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT)); 5727e035230SIlan Elias } 5737e035230SIlan Elias 57490d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev) 57590d78c13SJulien Lefrique { 57690d78c13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 57790d78c13SJulien Lefrique int rc; 57890d78c13SJulien Lefrique __u8 val; 57990d78c13SJulien Lefrique 58090d78c13SJulien Lefrique val = NCI_LA_SEL_INFO_NFC_DEP_MASK; 58190d78c13SJulien Lefrique 58290d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val); 58390d78c13SJulien Lefrique if (rc) 58490d78c13SJulien Lefrique return rc; 58590d78c13SJulien Lefrique 58690d78c13SJulien Lefrique val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK; 58790d78c13SJulien Lefrique 58890d78c13SJulien Lefrique rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val); 58990d78c13SJulien Lefrique if (rc) 59090d78c13SJulien Lefrique return rc; 59190d78c13SJulien Lefrique 59290d78c13SJulien Lefrique val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424; 59390d78c13SJulien Lefrique 59490d78c13SJulien Lefrique return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val); 59590d78c13SJulien Lefrique } 59690d78c13SJulien Lefrique 597fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev, 598fe7c5800SSamuel Ortiz __u32 im_protocols, __u32 tm_protocols) 5996a2968aaSIlan Elias { 6006a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 601772dccf4SJulien Lefrique struct nci_rf_discover_param param; 6026a2968aaSIlan Elias int rc; 6036a2968aaSIlan Elias 604019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_DISCOVERY) || 605019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) { 606ed1e0ad8SJoe Perches pr_err("unable to start poll, since poll is already active\n"); 6076a2968aaSIlan Elias return -EBUSY; 6086a2968aaSIlan Elias } 6096a2968aaSIlan Elias 610de054799SIlan Elias if (ndev->target_active_prot) { 611ed1e0ad8SJoe Perches pr_err("there is an active target\n"); 612de054799SIlan Elias return -EBUSY; 613de054799SIlan Elias } 614de054799SIlan Elias 615019c4fbaSIlan Elias if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) || 616019c4fbaSIlan Elias (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) { 617019c4fbaSIlan Elias pr_debug("target active or w4 select, implicitly deactivate\n"); 6186a2968aaSIlan Elias 6199295b5b5SChristophe Ricard rc = nci_request(ndev, nci_rf_deactivate_req, 6209295b5b5SChristophe Ricard NCI_DEACTIVATE_TYPE_IDLE_MODE, 6216a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 6226a2968aaSIlan Elias if (rc) 6236a2968aaSIlan Elias return -EBUSY; 6246a2968aaSIlan Elias } 6256a2968aaSIlan Elias 626529ee066SJulien Lefrique if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { 6277e035230SIlan Elias rc = nci_set_local_general_bytes(nfc_dev); 6287e035230SIlan Elias if (rc) { 6297e035230SIlan Elias pr_err("failed to set local general bytes\n"); 6307e035230SIlan Elias return rc; 6317e035230SIlan Elias } 6327e035230SIlan Elias } 6337e035230SIlan Elias 63490d78c13SJulien Lefrique if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { 63590d78c13SJulien Lefrique rc = nci_set_listen_parameters(nfc_dev); 63690d78c13SJulien Lefrique if (rc) 63790d78c13SJulien Lefrique pr_err("failed to set listen parameters\n"); 63890d78c13SJulien Lefrique } 63990d78c13SJulien Lefrique 640772dccf4SJulien Lefrique param.im_protocols = im_protocols; 641772dccf4SJulien Lefrique param.tm_protocols = tm_protocols; 642772dccf4SJulien Lefrique rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)¶m, 6436a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 6446a2968aaSIlan Elias 6456a2968aaSIlan Elias if (!rc) 646fe7c5800SSamuel Ortiz ndev->poll_prots = im_protocols; 6476a2968aaSIlan Elias 6486a2968aaSIlan Elias return rc; 6496a2968aaSIlan Elias } 6506a2968aaSIlan Elias 6516a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev) 6526a2968aaSIlan Elias { 6536a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 6546a2968aaSIlan Elias 655019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_DISCOVERY) && 656019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) { 657ed1e0ad8SJoe Perches pr_err("unable to stop poll, since poll is not active\n"); 6586a2968aaSIlan Elias return; 6596a2968aaSIlan Elias } 6606a2968aaSIlan Elias 6619295b5b5SChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE, 6626a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 6636a2968aaSIlan Elias } 6646a2968aaSIlan Elias 66590099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev, 66690099433SEric Lapuyade struct nfc_target *target, __u32 protocol) 6676a2968aaSIlan Elias { 6686a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 669019c4fbaSIlan Elias struct nci_rf_discover_select_param param; 67090099433SEric Lapuyade struct nfc_target *nci_target = NULL; 671019c4fbaSIlan Elias int i; 672019c4fbaSIlan Elias int rc = 0; 6736a2968aaSIlan Elias 67490099433SEric Lapuyade pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol); 6756a2968aaSIlan Elias 676019c4fbaSIlan Elias if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) && 677019c4fbaSIlan Elias (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) { 678ed1e0ad8SJoe Perches pr_err("there is no available target to activate\n"); 6796a2968aaSIlan Elias return -EINVAL; 6806a2968aaSIlan Elias } 6816a2968aaSIlan Elias 6826a2968aaSIlan Elias if (ndev->target_active_prot) { 683ed1e0ad8SJoe Perches pr_err("there is already an active target\n"); 6846a2968aaSIlan Elias return -EBUSY; 6856a2968aaSIlan Elias } 6866a2968aaSIlan Elias 687019c4fbaSIlan Elias for (i = 0; i < ndev->n_targets; i++) { 68890099433SEric Lapuyade if (ndev->targets[i].idx == target->idx) { 68990099433SEric Lapuyade nci_target = &ndev->targets[i]; 690019c4fbaSIlan Elias break; 691019c4fbaSIlan Elias } 692019c4fbaSIlan Elias } 693019c4fbaSIlan Elias 69490099433SEric Lapuyade if (!nci_target) { 695019c4fbaSIlan Elias pr_err("unable to find the selected target\n"); 696019c4fbaSIlan Elias return -EINVAL; 697019c4fbaSIlan Elias } 698019c4fbaSIlan Elias 69990099433SEric Lapuyade if (!(nci_target->supported_protocols & (1 << protocol))) { 700ed1e0ad8SJoe Perches pr_err("target does not support the requested protocol 0x%x\n", 7016a2968aaSIlan Elias protocol); 7026a2968aaSIlan Elias return -EINVAL; 7036a2968aaSIlan Elias } 7046a2968aaSIlan Elias 705019c4fbaSIlan Elias if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) { 70690099433SEric Lapuyade param.rf_discovery_id = nci_target->logical_idx; 7076a2968aaSIlan Elias 708019c4fbaSIlan Elias if (protocol == NFC_PROTO_JEWEL) 709019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T1T; 710019c4fbaSIlan Elias else if (protocol == NFC_PROTO_MIFARE) 711019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T2T; 712019c4fbaSIlan Elias else if (protocol == NFC_PROTO_FELICA) 713019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_T3T; 71401d719a2SSamuel Ortiz else if (protocol == NFC_PROTO_ISO14443 || 71501d719a2SSamuel Ortiz protocol == NFC_PROTO_ISO14443_B) 716019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 717019c4fbaSIlan Elias else 718019c4fbaSIlan Elias param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 719019c4fbaSIlan Elias 720019c4fbaSIlan Elias rc = nci_request(ndev, nci_rf_discover_select_req, 721019c4fbaSIlan Elias (unsigned long)¶m, 722019c4fbaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT)); 723019c4fbaSIlan Elias } 724019c4fbaSIlan Elias 725019c4fbaSIlan Elias if (!rc) 726019c4fbaSIlan Elias ndev->target_active_prot = protocol; 727019c4fbaSIlan Elias 728019c4fbaSIlan Elias return rc; 7296a2968aaSIlan Elias } 7306a2968aaSIlan Elias 73190099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev, 73290099433SEric Lapuyade struct nfc_target *target) 7336a2968aaSIlan Elias { 7346a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 7356a2968aaSIlan Elias 736767f19aeSIlan Elias pr_debug("entry\n"); 7376a2968aaSIlan Elias 7386a2968aaSIlan Elias if (!ndev->target_active_prot) { 739ed1e0ad8SJoe Perches pr_err("unable to deactivate target, no active target\n"); 7406a2968aaSIlan Elias return; 7416a2968aaSIlan Elias } 7426a2968aaSIlan Elias 7436a2968aaSIlan Elias ndev->target_active_prot = 0; 7446a2968aaSIlan Elias 7458939e47fSIlan Elias if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) { 7469295b5b5SChristophe Ricard nci_request(ndev, nci_rf_deactivate_req, 7479295b5b5SChristophe Ricard NCI_DEACTIVATE_TYPE_SLEEP_MODE, 7486a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 7496a2968aaSIlan Elias } 7506a2968aaSIlan Elias } 7516a2968aaSIlan Elias 752767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, 753767f19aeSIlan Elias __u8 comm_mode, __u8 *gb, size_t gb_len) 754767f19aeSIlan Elias { 755767f19aeSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 756767f19aeSIlan Elias int rc; 757767f19aeSIlan Elias 758767f19aeSIlan Elias pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode); 759767f19aeSIlan Elias 760767f19aeSIlan Elias rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP); 761767f19aeSIlan Elias if (rc) 762767f19aeSIlan Elias return rc; 763767f19aeSIlan Elias 764767f19aeSIlan Elias rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb, 765767f19aeSIlan Elias ndev->remote_gb_len); 766767f19aeSIlan Elias if (!rc) 767767f19aeSIlan Elias rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE, 768767f19aeSIlan Elias NFC_RF_INITIATOR); 769767f19aeSIlan Elias 770767f19aeSIlan Elias return rc; 771767f19aeSIlan Elias } 772767f19aeSIlan Elias 773767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev) 774767f19aeSIlan Elias { 775d7979e13SJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 776d7979e13SJulien Lefrique int rc; 777d7979e13SJulien Lefrique 778767f19aeSIlan Elias pr_debug("entry\n"); 779767f19aeSIlan Elias 780d7979e13SJulien Lefrique if (nfc_dev->rf_mode == NFC_RF_INITIATOR) { 781767f19aeSIlan Elias nci_deactivate_target(nfc_dev, NULL); 782d7979e13SJulien Lefrique } else { 783d7979e13SJulien Lefrique if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE || 784d7979e13SJulien Lefrique atomic_read(&ndev->state) == NCI_DISCOVERY) { 785d7979e13SJulien Lefrique nci_request(ndev, nci_rf_deactivate_req, 0, 786d7979e13SJulien Lefrique msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 787d7979e13SJulien Lefrique } 788d7979e13SJulien Lefrique 789d7979e13SJulien Lefrique rc = nfc_tm_deactivated(nfc_dev); 790d7979e13SJulien Lefrique if (rc) 791d7979e13SJulien Lefrique pr_err("error when signaling tm deactivation\n"); 792d7979e13SJulien Lefrique } 793767f19aeSIlan Elias 794767f19aeSIlan Elias return 0; 795767f19aeSIlan Elias } 796767f19aeSIlan Elias 797767f19aeSIlan Elias 798be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target, 7996a2968aaSIlan Elias struct sk_buff *skb, 800eb9bc6e9SSamuel Ortiz data_exchange_cb_t cb, void *cb_context) 8016a2968aaSIlan Elias { 8026a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 80338f04c6bSIlan Elias int rc; 8044aeee687SChristophe Ricard struct nci_conn_info *conn_info; 8054aeee687SChristophe Ricard 80612bdf27dSChristophe Ricard conn_info = ndev->rf_conn_info; 8074aeee687SChristophe Ricard if (!conn_info) 8084aeee687SChristophe Ricard return -EPROTO; 8096a2968aaSIlan Elias 81090099433SEric Lapuyade pr_debug("target_idx %d, len %d\n", target->idx, skb->len); 8116a2968aaSIlan Elias 8126a2968aaSIlan Elias if (!ndev->target_active_prot) { 813ed1e0ad8SJoe Perches pr_err("unable to exchange data, no active target\n"); 8146a2968aaSIlan Elias return -EINVAL; 8156a2968aaSIlan Elias } 8166a2968aaSIlan Elias 81738f04c6bSIlan Elias if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 81838f04c6bSIlan Elias return -EBUSY; 81938f04c6bSIlan Elias 8206a2968aaSIlan Elias /* store cb and context to be used on receiving data */ 8214aeee687SChristophe Ricard conn_info->data_exchange_cb = cb; 8224aeee687SChristophe Ricard conn_info->data_exchange_cb_context = cb_context; 8236a2968aaSIlan Elias 824e8c0dacdSIlan Elias rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 82538f04c6bSIlan Elias if (rc) 82638f04c6bSIlan Elias clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 82738f04c6bSIlan Elias 82838f04c6bSIlan Elias return rc; 8296a2968aaSIlan Elias } 8306a2968aaSIlan Elias 831485f442fSJulien Lefrique static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) 832485f442fSJulien Lefrique { 833485f442fSJulien Lefrique struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 834485f442fSJulien Lefrique int rc; 835485f442fSJulien Lefrique 836485f442fSJulien Lefrique rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 837485f442fSJulien Lefrique if (rc) 838485f442fSJulien Lefrique pr_err("unable to send data\n"); 839485f442fSJulien Lefrique 840485f442fSJulien Lefrique return rc; 841485f442fSJulien Lefrique } 842485f442fSJulien Lefrique 8430a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx) 8440a946301SSamuel Ortiz { 84593bca2bfSChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 84693bca2bfSChristophe Ricard 84793bca2bfSChristophe Ricard if (ndev->ops->enable_se) 84893bca2bfSChristophe Ricard return ndev->ops->enable_se(ndev, se_idx); 84993bca2bfSChristophe Ricard 8500a946301SSamuel Ortiz return 0; 8510a946301SSamuel Ortiz } 8520a946301SSamuel Ortiz 8530a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx) 8540a946301SSamuel Ortiz { 855e9ef9431SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 856e9ef9431SChristophe Ricard 857e9ef9431SChristophe Ricard if (ndev->ops->disable_se) 858e9ef9431SChristophe Ricard return ndev->ops->disable_se(ndev, se_idx); 859e9ef9431SChristophe Ricard 8600a946301SSamuel Ortiz return 0; 8610a946301SSamuel Ortiz } 8620a946301SSamuel Ortiz 8630a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev) 8640a946301SSamuel Ortiz { 865ba4db551SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 866ba4db551SChristophe Ricard 867ba4db551SChristophe Ricard if (ndev->ops->discover_se) 868ba4db551SChristophe Ricard return ndev->ops->discover_se(ndev); 869ba4db551SChristophe Ricard 8700a946301SSamuel Ortiz return 0; 8710a946301SSamuel Ortiz } 8720a946301SSamuel Ortiz 873a688bf55SChristophe Ricard static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, 874a688bf55SChristophe Ricard u8 *apdu, size_t apdu_length, 875a688bf55SChristophe Ricard se_io_cb_t cb, void *cb_context) 876a688bf55SChristophe Ricard { 877a688bf55SChristophe Ricard struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 878a688bf55SChristophe Ricard 879a688bf55SChristophe Ricard if (ndev->ops->se_io) 880a688bf55SChristophe Ricard return ndev->ops->se_io(ndev, se_idx, apdu, 881a688bf55SChristophe Ricard apdu_length, cb, cb_context); 882a688bf55SChristophe Ricard 883a688bf55SChristophe Ricard return 0; 884a688bf55SChristophe Ricard } 885a688bf55SChristophe Ricard 8866a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = { 8876a2968aaSIlan Elias .dev_up = nci_dev_up, 8886a2968aaSIlan Elias .dev_down = nci_dev_down, 8896a2968aaSIlan Elias .start_poll = nci_start_poll, 8906a2968aaSIlan Elias .stop_poll = nci_stop_poll, 891767f19aeSIlan Elias .dep_link_up = nci_dep_link_up, 892767f19aeSIlan Elias .dep_link_down = nci_dep_link_down, 8936a2968aaSIlan Elias .activate_target = nci_activate_target, 8946a2968aaSIlan Elias .deactivate_target = nci_deactivate_target, 895be9ae4ceSSamuel Ortiz .im_transceive = nci_transceive, 896485f442fSJulien Lefrique .tm_send = nci_tm_send, 8970a946301SSamuel Ortiz .enable_se = nci_enable_se, 8980a946301SSamuel Ortiz .disable_se = nci_disable_se, 8990a946301SSamuel Ortiz .discover_se = nci_discover_se, 900a688bf55SChristophe Ricard .se_io = nci_se_io, 9016a2968aaSIlan Elias }; 9026a2968aaSIlan Elias 9036a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */ 9046a2968aaSIlan Elias /** 9056a2968aaSIlan Elias * nci_allocate_device - allocate a new nci device 9066a2968aaSIlan Elias * 9076a2968aaSIlan Elias * @ops: device operations 9086a2968aaSIlan Elias * @supported_protocols: NFC protocols supported by the device 9096a2968aaSIlan Elias */ 9106a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops, 9116a2968aaSIlan Elias __u32 supported_protocols, 912eb9bc6e9SSamuel Ortiz int tx_headroom, int tx_tailroom) 9136a2968aaSIlan Elias { 9148ebafde0SDan Carpenter struct nci_dev *ndev; 9156a2968aaSIlan Elias 91624bf3304SJoe Perches pr_debug("supported_protocols 0x%x\n", supported_protocols); 9176a2968aaSIlan Elias 9186a2968aaSIlan Elias if (!ops->open || !ops->close || !ops->send) 9198ebafde0SDan Carpenter return NULL; 9206a2968aaSIlan Elias 9216a2968aaSIlan Elias if (!supported_protocols) 9228ebafde0SDan Carpenter return NULL; 9236a2968aaSIlan Elias 9246a2968aaSIlan Elias ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 9256a2968aaSIlan Elias if (!ndev) 9268ebafde0SDan Carpenter return NULL; 9276a2968aaSIlan Elias 9286a2968aaSIlan Elias ndev->ops = ops; 9296a2968aaSIlan Elias ndev->tx_headroom = tx_headroom; 9306a2968aaSIlan Elias ndev->tx_tailroom = tx_tailroom; 9319bec44bfSAxel Lin init_completion(&ndev->req_completion); 9326a2968aaSIlan Elias 9336a2968aaSIlan Elias ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 9346a2968aaSIlan Elias supported_protocols, 9356a2968aaSIlan Elias tx_headroom + NCI_DATA_HDR_SIZE, 9366a2968aaSIlan Elias tx_tailroom); 9376a2968aaSIlan Elias if (!ndev->nfc_dev) 93811f54f22SChristophe Ricard goto free_nci; 93911f54f22SChristophe Ricard 94011f54f22SChristophe Ricard ndev->hci_dev = nci_hci_allocate(ndev); 94111f54f22SChristophe Ricard if (!ndev->hci_dev) 94211f54f22SChristophe Ricard goto free_nfc; 9436a2968aaSIlan Elias 9446a2968aaSIlan Elias nfc_set_drvdata(ndev->nfc_dev, ndev); 9456a2968aaSIlan Elias 9468ebafde0SDan Carpenter return ndev; 9476a2968aaSIlan Elias 94811f54f22SChristophe Ricard free_nfc: 94911f54f22SChristophe Ricard kfree(ndev->nfc_dev); 95011f54f22SChristophe Ricard 95111f54f22SChristophe Ricard free_nci: 9526a2968aaSIlan Elias kfree(ndev); 9538ebafde0SDan Carpenter return NULL; 9546a2968aaSIlan Elias } 9556a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device); 9566a2968aaSIlan Elias 9576a2968aaSIlan Elias /** 9586a2968aaSIlan Elias * nci_free_device - deallocate nci device 9596a2968aaSIlan Elias * 9606a2968aaSIlan Elias * @ndev: The nci device to deallocate 9616a2968aaSIlan Elias */ 9626a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev) 9636a2968aaSIlan Elias { 9646a2968aaSIlan Elias nfc_free_device(ndev->nfc_dev); 9656a2968aaSIlan Elias kfree(ndev); 9666a2968aaSIlan Elias } 9676a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device); 9686a2968aaSIlan Elias 9696a2968aaSIlan Elias /** 9706a2968aaSIlan Elias * nci_register_device - register a nci device in the nfc subsystem 9716a2968aaSIlan Elias * 9726a2968aaSIlan Elias * @dev: The nci device to register 9736a2968aaSIlan Elias */ 9746a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev) 9756a2968aaSIlan Elias { 9766a2968aaSIlan Elias int rc; 9776a2968aaSIlan Elias struct device *dev = &ndev->nfc_dev->dev; 9786a2968aaSIlan Elias char name[32]; 9796a2968aaSIlan Elias 9806a2968aaSIlan Elias ndev->flags = 0; 9816a2968aaSIlan Elias 9826a2968aaSIlan Elias INIT_WORK(&ndev->cmd_work, nci_cmd_work); 9836a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 9846a2968aaSIlan Elias ndev->cmd_wq = create_singlethread_workqueue(name); 9856a2968aaSIlan Elias if (!ndev->cmd_wq) { 9866a2968aaSIlan Elias rc = -ENOMEM; 9873c1c0f5dSVincent Cuissard goto exit; 9886a2968aaSIlan Elias } 9896a2968aaSIlan Elias 9906a2968aaSIlan Elias INIT_WORK(&ndev->rx_work, nci_rx_work); 9916a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 9926a2968aaSIlan Elias ndev->rx_wq = create_singlethread_workqueue(name); 9936a2968aaSIlan Elias if (!ndev->rx_wq) { 9946a2968aaSIlan Elias rc = -ENOMEM; 9956a2968aaSIlan Elias goto destroy_cmd_wq_exit; 9966a2968aaSIlan Elias } 9976a2968aaSIlan Elias 9986a2968aaSIlan Elias INIT_WORK(&ndev->tx_work, nci_tx_work); 9996a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 10006a2968aaSIlan Elias ndev->tx_wq = create_singlethread_workqueue(name); 10016a2968aaSIlan Elias if (!ndev->tx_wq) { 10026a2968aaSIlan Elias rc = -ENOMEM; 10036a2968aaSIlan Elias goto destroy_rx_wq_exit; 10046a2968aaSIlan Elias } 10056a2968aaSIlan Elias 10066a2968aaSIlan Elias skb_queue_head_init(&ndev->cmd_q); 10076a2968aaSIlan Elias skb_queue_head_init(&ndev->rx_q); 10086a2968aaSIlan Elias skb_queue_head_init(&ndev->tx_q); 10096a2968aaSIlan Elias 10106a2968aaSIlan Elias setup_timer(&ndev->cmd_timer, nci_cmd_timer, 10116a2968aaSIlan Elias (unsigned long) ndev); 1012c4bf98b2SIlan Elias setup_timer(&ndev->data_timer, nci_data_timer, 1013c4bf98b2SIlan Elias (unsigned long) ndev); 10146a2968aaSIlan Elias 10156a2968aaSIlan Elias mutex_init(&ndev->req_lock); 10164aeee687SChristophe Ricard INIT_LIST_HEAD(&ndev->conn_info_list); 10176a2968aaSIlan Elias 10183c1c0f5dSVincent Cuissard rc = nfc_register_device(ndev->nfc_dev); 10193c1c0f5dSVincent Cuissard if (rc) 10203c1c0f5dSVincent Cuissard goto destroy_rx_wq_exit; 10213c1c0f5dSVincent Cuissard 10226a2968aaSIlan Elias goto exit; 10236a2968aaSIlan Elias 10246a2968aaSIlan Elias destroy_rx_wq_exit: 10256a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 10266a2968aaSIlan Elias 10276a2968aaSIlan Elias destroy_cmd_wq_exit: 10286a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 10296a2968aaSIlan Elias 10306a2968aaSIlan Elias exit: 10316a2968aaSIlan Elias return rc; 10326a2968aaSIlan Elias } 10336a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device); 10346a2968aaSIlan Elias 10356a2968aaSIlan Elias /** 10366a2968aaSIlan Elias * nci_unregister_device - unregister a nci device in the nfc subsystem 10376a2968aaSIlan Elias * 10386a2968aaSIlan Elias * @dev: The nci device to unregister 10396a2968aaSIlan Elias */ 10406a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev) 10416a2968aaSIlan Elias { 10424aeee687SChristophe Ricard struct nci_conn_info *conn_info, *n; 10434aeee687SChristophe Ricard 10446a2968aaSIlan Elias nci_close_device(ndev); 10456a2968aaSIlan Elias 10466a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 10476a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 10486a2968aaSIlan Elias destroy_workqueue(ndev->tx_wq); 10496a2968aaSIlan Elias 10504aeee687SChristophe Ricard list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) { 10514aeee687SChristophe Ricard list_del(&conn_info->list); 10524aeee687SChristophe Ricard /* conn_info is allocated with devm_kzalloc */ 10534aeee687SChristophe Ricard } 10544aeee687SChristophe Ricard 10556a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 10566a2968aaSIlan Elias } 10576a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device); 10586a2968aaSIlan Elias 10596a2968aaSIlan Elias /** 10606a2968aaSIlan Elias * nci_recv_frame - receive frame from NCI drivers 10616a2968aaSIlan Elias * 10621095e69fSFrederic Danis * @ndev: The nci device 10636a2968aaSIlan Elias * @skb: The sk_buff to receive 10646a2968aaSIlan Elias */ 10651095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) 10666a2968aaSIlan Elias { 106724bf3304SJoe Perches pr_debug("len %d\n", skb->len); 10686a2968aaSIlan Elias 1069874934f4SSzymon Janc if (!ndev || (!test_bit(NCI_UP, &ndev->flags) && 1070874934f4SSzymon Janc !test_bit(NCI_INIT, &ndev->flags))) { 10716a2968aaSIlan Elias kfree_skb(skb); 10726a2968aaSIlan Elias return -ENXIO; 10736a2968aaSIlan Elias } 10746a2968aaSIlan Elias 10756a2968aaSIlan Elias /* Queue frame for rx worker thread */ 10766a2968aaSIlan Elias skb_queue_tail(&ndev->rx_q, skb); 10776a2968aaSIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 10786a2968aaSIlan Elias 10796a2968aaSIlan Elias return 0; 10806a2968aaSIlan Elias } 10816a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame); 10826a2968aaSIlan Elias 10831095e69fSFrederic Danis static int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb) 10846a2968aaSIlan Elias { 108524bf3304SJoe Perches pr_debug("len %d\n", skb->len); 10866a2968aaSIlan Elias 10876a2968aaSIlan Elias if (!ndev) { 10886a2968aaSIlan Elias kfree_skb(skb); 10896a2968aaSIlan Elias return -ENODEV; 10906a2968aaSIlan Elias } 10916a2968aaSIlan Elias 10926a2968aaSIlan Elias /* Get rid of skb owner, prior to sending to the driver. */ 10936a2968aaSIlan Elias skb_orphan(skb); 10946a2968aaSIlan Elias 109505158296SHiren Tandel /* Send copy to sniffer */ 109605158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 109705158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_TX); 109805158296SHiren Tandel 10991095e69fSFrederic Danis return ndev->ops->send(ndev, skb); 11006a2968aaSIlan Elias } 11016a2968aaSIlan Elias 11026a2968aaSIlan Elias /* Send NCI command */ 11036a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 11046a2968aaSIlan Elias { 11056a2968aaSIlan Elias struct nci_ctrl_hdr *hdr; 11066a2968aaSIlan Elias struct sk_buff *skb; 11076a2968aaSIlan Elias 110824bf3304SJoe Perches pr_debug("opcode 0x%x, plen %d\n", opcode, plen); 11096a2968aaSIlan Elias 11106a2968aaSIlan Elias skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 11116a2968aaSIlan Elias if (!skb) { 1112ed1e0ad8SJoe Perches pr_err("no memory for command\n"); 11136a2968aaSIlan Elias return -ENOMEM; 11146a2968aaSIlan Elias } 11156a2968aaSIlan Elias 11166a2968aaSIlan Elias hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); 11176a2968aaSIlan Elias hdr->gid = nci_opcode_gid(opcode); 11186a2968aaSIlan Elias hdr->oid = nci_opcode_oid(opcode); 11196a2968aaSIlan Elias hdr->plen = plen; 11206a2968aaSIlan Elias 11216a2968aaSIlan Elias nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 11226a2968aaSIlan Elias nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 11236a2968aaSIlan Elias 11246a2968aaSIlan Elias if (plen) 11256a2968aaSIlan Elias memcpy(skb_put(skb, plen), payload, plen); 11266a2968aaSIlan Elias 11276a2968aaSIlan Elias skb_queue_tail(&ndev->cmd_q, skb); 11286a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 11296a2968aaSIlan Elias 11306a2968aaSIlan Elias return 0; 11316a2968aaSIlan Elias } 11326a2968aaSIlan Elias 11336a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */ 11346a2968aaSIlan Elias 11356a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work) 11366a2968aaSIlan Elias { 11376a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 11384aeee687SChristophe Ricard struct nci_conn_info *conn_info; 11396a2968aaSIlan Elias struct sk_buff *skb; 11406a2968aaSIlan Elias 11414aeee687SChristophe Ricard conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id); 11424aeee687SChristophe Ricard if (!conn_info) 11434aeee687SChristophe Ricard return; 11444aeee687SChristophe Ricard 11454aeee687SChristophe Ricard pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt)); 11466a2968aaSIlan Elias 11476a2968aaSIlan Elias /* Send queued tx data */ 11484aeee687SChristophe Ricard while (atomic_read(&conn_info->credits_cnt)) { 11496a2968aaSIlan Elias skb = skb_dequeue(&ndev->tx_q); 11506a2968aaSIlan Elias if (!skb) 11516a2968aaSIlan Elias return; 11526a2968aaSIlan Elias 1153db98c829SIlan Elias /* Check if data flow control is used */ 11544aeee687SChristophe Ricard if (atomic_read(&conn_info->credits_cnt) != 1155db98c829SIlan Elias NCI_DATA_FLOW_CONTROL_NOT_USED) 11564aeee687SChristophe Ricard atomic_dec(&conn_info->credits_cnt); 11576a2968aaSIlan Elias 115820c239c1SJoe Perches pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 11596a2968aaSIlan Elias nci_pbf(skb->data), 11606a2968aaSIlan Elias nci_conn_id(skb->data), 11616a2968aaSIlan Elias nci_plen(skb->data)); 11626a2968aaSIlan Elias 11631095e69fSFrederic Danis nci_send_frame(ndev, skb); 1164c4bf98b2SIlan Elias 1165c4bf98b2SIlan Elias mod_timer(&ndev->data_timer, 1166c4bf98b2SIlan Elias jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT)); 11676a2968aaSIlan Elias } 11686a2968aaSIlan Elias } 11696a2968aaSIlan Elias 11706a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */ 11716a2968aaSIlan Elias 11726a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work) 11736a2968aaSIlan Elias { 11746a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 11756a2968aaSIlan Elias struct sk_buff *skb; 11766a2968aaSIlan Elias 11776a2968aaSIlan Elias while ((skb = skb_dequeue(&ndev->rx_q))) { 117805158296SHiren Tandel 117905158296SHiren Tandel /* Send copy to sniffer */ 118005158296SHiren Tandel nfc_send_to_raw_sock(ndev->nfc_dev, skb, 118105158296SHiren Tandel RAW_PAYLOAD_NCI, NFC_DIRECTION_RX); 118205158296SHiren Tandel 11836a2968aaSIlan Elias /* Process frame */ 11846a2968aaSIlan Elias switch (nci_mt(skb->data)) { 11856a2968aaSIlan Elias case NCI_MT_RSP_PKT: 11866a2968aaSIlan Elias nci_rsp_packet(ndev, skb); 11876a2968aaSIlan Elias break; 11886a2968aaSIlan Elias 11896a2968aaSIlan Elias case NCI_MT_NTF_PKT: 11906a2968aaSIlan Elias nci_ntf_packet(ndev, skb); 11916a2968aaSIlan Elias break; 11926a2968aaSIlan Elias 11936a2968aaSIlan Elias case NCI_MT_DATA_PKT: 11946a2968aaSIlan Elias nci_rx_data_packet(ndev, skb); 11956a2968aaSIlan Elias break; 11966a2968aaSIlan Elias 11976a2968aaSIlan Elias default: 1198ed1e0ad8SJoe Perches pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 11996a2968aaSIlan Elias kfree_skb(skb); 12006a2968aaSIlan Elias break; 12016a2968aaSIlan Elias } 12026a2968aaSIlan Elias } 1203c4bf98b2SIlan Elias 1204c4bf98b2SIlan Elias /* check if a data exchange timout has occurred */ 1205c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) { 1206c4bf98b2SIlan Elias /* complete the data exchange transaction, if exists */ 1207c4bf98b2SIlan Elias if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 12084aeee687SChristophe Ricard nci_data_exchange_complete(ndev, NULL, 12094aeee687SChristophe Ricard ndev->cur_conn_id, 12104aeee687SChristophe Ricard -ETIMEDOUT); 1211c4bf98b2SIlan Elias 1212c4bf98b2SIlan Elias clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); 1213c4bf98b2SIlan Elias } 12146a2968aaSIlan Elias } 12156a2968aaSIlan Elias 12166a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */ 12176a2968aaSIlan Elias 12186a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work) 12196a2968aaSIlan Elias { 12206a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 12216a2968aaSIlan Elias struct sk_buff *skb; 12226a2968aaSIlan Elias 122324bf3304SJoe Perches pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 12246a2968aaSIlan Elias 12256a2968aaSIlan Elias /* Send queued command */ 12266a2968aaSIlan Elias if (atomic_read(&ndev->cmd_cnt)) { 12276a2968aaSIlan Elias skb = skb_dequeue(&ndev->cmd_q); 12286a2968aaSIlan Elias if (!skb) 12296a2968aaSIlan Elias return; 12306a2968aaSIlan Elias 12316a2968aaSIlan Elias atomic_dec(&ndev->cmd_cnt); 12326a2968aaSIlan Elias 123320c239c1SJoe Perches pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 12346a2968aaSIlan Elias nci_pbf(skb->data), 12356a2968aaSIlan Elias nci_opcode_gid(nci_opcode(skb->data)), 12366a2968aaSIlan Elias nci_opcode_oid(nci_opcode(skb->data)), 12376a2968aaSIlan Elias nci_plen(skb->data)); 12386a2968aaSIlan Elias 12391095e69fSFrederic Danis nci_send_frame(ndev, skb); 12406a2968aaSIlan Elias 12416a2968aaSIlan Elias mod_timer(&ndev->cmd_timer, 12426a2968aaSIlan Elias jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 12436a2968aaSIlan Elias } 12446a2968aaSIlan Elias } 12458a70e7f8SDave Jones 12468a70e7f8SDave Jones MODULE_LICENSE("GPL"); 1247