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. 66a2968aaSIlan Elias * 76a2968aaSIlan Elias * Written by Ilan Elias <ilane@ti.com> 86a2968aaSIlan Elias * 96a2968aaSIlan Elias * Acknowledgements: 106a2968aaSIlan Elias * This file is based on hci_core.c, which was written 116a2968aaSIlan Elias * by Maxim Krasnyansky. 126a2968aaSIlan Elias * 136a2968aaSIlan Elias * This program is free software; you can redistribute it and/or modify 146a2968aaSIlan Elias * it under the terms of the GNU General Public License version 2 156a2968aaSIlan Elias * as published by the Free Software Foundation 166a2968aaSIlan Elias * 176a2968aaSIlan Elias * This program is distributed in the hope that it will be useful, 186a2968aaSIlan Elias * but WITHOUT ANY WARRANTY; without even the implied warranty of 196a2968aaSIlan Elias * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 206a2968aaSIlan Elias * GNU General Public License for more details. 216a2968aaSIlan Elias * 226a2968aaSIlan Elias * You should have received a copy of the GNU General Public License 236a2968aaSIlan Elias * along with this program; if not, write to the Free Software 246a2968aaSIlan Elias * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 256a2968aaSIlan Elias * 266a2968aaSIlan Elias */ 276a2968aaSIlan Elias 28ed1e0ad8SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 29ed1e0ad8SJoe Perches 306a2968aaSIlan Elias #include <linux/types.h> 316a2968aaSIlan Elias #include <linux/workqueue.h> 326a2968aaSIlan Elias #include <linux/completion.h> 33bc3b2d7fSPaul Gortmaker #include <linux/export.h> 346a2968aaSIlan Elias #include <linux/sched.h> 356a2968aaSIlan Elias #include <linux/bitops.h> 366a2968aaSIlan Elias #include <linux/skbuff.h> 376a2968aaSIlan Elias 386a2968aaSIlan Elias #include "../nfc.h" 396a2968aaSIlan Elias #include <net/nfc/nci.h> 406a2968aaSIlan Elias #include <net/nfc/nci_core.h> 416a2968aaSIlan Elias #include <linux/nfc.h> 426a2968aaSIlan Elias 436a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work); 446a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work); 456a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work); 466a2968aaSIlan Elias 476a2968aaSIlan Elias /* ---- NCI requests ---- */ 486a2968aaSIlan Elias 496a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result) 506a2968aaSIlan Elias { 516a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 526a2968aaSIlan Elias ndev->req_result = result; 536a2968aaSIlan Elias ndev->req_status = NCI_REQ_DONE; 546a2968aaSIlan Elias complete(&ndev->req_completion); 556a2968aaSIlan Elias } 566a2968aaSIlan Elias } 576a2968aaSIlan Elias 586a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err) 596a2968aaSIlan Elias { 606a2968aaSIlan Elias if (ndev->req_status == NCI_REQ_PEND) { 616a2968aaSIlan Elias ndev->req_result = err; 626a2968aaSIlan Elias ndev->req_status = NCI_REQ_CANCELED; 636a2968aaSIlan Elias complete(&ndev->req_completion); 646a2968aaSIlan Elias } 656a2968aaSIlan Elias } 666a2968aaSIlan Elias 676a2968aaSIlan Elias /* Execute request and wait for completion. */ 686a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev, 696a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 706a2968aaSIlan Elias unsigned long opt, 716a2968aaSIlan Elias __u32 timeout) 726a2968aaSIlan Elias { 736a2968aaSIlan Elias int rc = 0; 746a2968aaSIlan Elias unsigned long completion_rc; 756a2968aaSIlan Elias 766a2968aaSIlan Elias ndev->req_status = NCI_REQ_PEND; 776a2968aaSIlan Elias 786a2968aaSIlan Elias init_completion(&ndev->req_completion); 796a2968aaSIlan Elias req(ndev, opt); 806a2968aaSIlan Elias completion_rc = wait_for_completion_interruptible_timeout( 816a2968aaSIlan Elias &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, 1136a2968aaSIlan Elias void (*req)(struct nci_dev *ndev, unsigned long opt), 1146a2968aaSIlan Elias unsigned long opt, __u32 timeout) 1156a2968aaSIlan Elias { 1166a2968aaSIlan Elias int rc; 1176a2968aaSIlan Elias 1186a2968aaSIlan Elias if (!test_bit(NCI_UP, &ndev->flags)) 1196a2968aaSIlan Elias return -ENETDOWN; 1206a2968aaSIlan Elias 1216a2968aaSIlan Elias /* Serialize all requests */ 1226a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 1236a2968aaSIlan Elias rc = __nci_request(ndev, req, opt, timeout); 1246a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 1256a2968aaSIlan Elias 1266a2968aaSIlan Elias return rc; 1276a2968aaSIlan Elias } 1286a2968aaSIlan Elias 1296a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) 1306a2968aaSIlan Elias { 131e8c0dacdSIlan Elias struct nci_core_reset_cmd cmd; 132e8c0dacdSIlan Elias 133e8c0dacdSIlan Elias cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG; 134e8c0dacdSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd); 1356a2968aaSIlan Elias } 1366a2968aaSIlan Elias 1376a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt) 1386a2968aaSIlan Elias { 1396a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); 1406a2968aaSIlan Elias } 1416a2968aaSIlan Elias 1426a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) 1436a2968aaSIlan Elias { 1442eb1dc10SIlan Elias struct nci_rf_disc_map_cmd cmd; 1452eb1dc10SIlan Elias struct disc_map_config *cfg = cmd.mapping_configs; 1462eb1dc10SIlan Elias __u8 *num = &cmd.num_mapping_configs; 1476a2968aaSIlan Elias int i; 1486a2968aaSIlan Elias 1496a2968aaSIlan Elias /* set rf mapping configurations */ 1502eb1dc10SIlan Elias *num = 0; 1516a2968aaSIlan Elias 1526a2968aaSIlan Elias /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ 1536a2968aaSIlan Elias for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { 1546a2968aaSIlan Elias if (ndev->supported_rf_interfaces[i] == 1556a2968aaSIlan Elias NCI_RF_INTERFACE_ISO_DEP) { 1562eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; 1572eb1dc10SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_BOTH; 1582eb1dc10SIlan Elias cfg[*num].rf_interface_type = NCI_RF_INTERFACE_ISO_DEP; 1592eb1dc10SIlan Elias (*num)++; 1606a2968aaSIlan Elias } else if (ndev->supported_rf_interfaces[i] == 1616a2968aaSIlan Elias NCI_RF_INTERFACE_NFC_DEP) { 1622eb1dc10SIlan Elias cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; 1632eb1dc10SIlan Elias cfg[*num].mode = NCI_DISC_MAP_MODE_BOTH; 1642eb1dc10SIlan Elias cfg[*num].rf_interface_type = NCI_RF_INTERFACE_NFC_DEP; 1652eb1dc10SIlan Elias (*num)++; 1666a2968aaSIlan Elias } 1676a2968aaSIlan Elias 1682eb1dc10SIlan Elias if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) 1696a2968aaSIlan Elias break; 1706a2968aaSIlan Elias } 1716a2968aaSIlan Elias 1726a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, 1732eb1dc10SIlan Elias (1 + ((*num)*sizeof(struct disc_map_config))), 1746a2968aaSIlan Elias &cmd); 1756a2968aaSIlan Elias } 1766a2968aaSIlan Elias 1776a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) 1786a2968aaSIlan Elias { 1796a2968aaSIlan Elias struct nci_rf_disc_cmd cmd; 1806a2968aaSIlan Elias __u32 protocols = opt; 1816a2968aaSIlan Elias 1826a2968aaSIlan Elias cmd.num_disc_configs = 0; 1836a2968aaSIlan Elias 1846a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 1856a2968aaSIlan Elias (protocols & NFC_PROTO_JEWEL_MASK 1866a2968aaSIlan Elias || protocols & NFC_PROTO_MIFARE_MASK 1876a2968aaSIlan Elias || protocols & NFC_PROTO_ISO14443_MASK 1886a2968aaSIlan Elias || protocols & NFC_PROTO_NFC_DEP_MASK)) { 1896a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].type = 1906a2968aaSIlan Elias NCI_DISCOVERY_TYPE_POLL_A_PASSIVE; 1916a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 1926a2968aaSIlan Elias cmd.num_disc_configs++; 1936a2968aaSIlan Elias } 1946a2968aaSIlan Elias 1956a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 1966a2968aaSIlan Elias (protocols & NFC_PROTO_ISO14443_MASK)) { 1976a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].type = 1986a2968aaSIlan Elias NCI_DISCOVERY_TYPE_POLL_B_PASSIVE; 1996a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2006a2968aaSIlan Elias cmd.num_disc_configs++; 2016a2968aaSIlan Elias } 2026a2968aaSIlan Elias 2036a2968aaSIlan Elias if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && 2046a2968aaSIlan Elias (protocols & NFC_PROTO_FELICA_MASK 2056a2968aaSIlan Elias || protocols & NFC_PROTO_NFC_DEP_MASK)) { 2066a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].type = 2076a2968aaSIlan Elias NCI_DISCOVERY_TYPE_POLL_F_PASSIVE; 2086a2968aaSIlan Elias cmd.disc_configs[cmd.num_disc_configs].frequency = 1; 2096a2968aaSIlan Elias cmd.num_disc_configs++; 2106a2968aaSIlan Elias } 2116a2968aaSIlan Elias 2126a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, 2136a2968aaSIlan Elias (1 + (cmd.num_disc_configs*sizeof(struct disc_config))), 2146a2968aaSIlan Elias &cmd); 2156a2968aaSIlan Elias } 2166a2968aaSIlan Elias 2176a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) 2186a2968aaSIlan Elias { 2196a2968aaSIlan Elias struct nci_rf_deactivate_cmd cmd; 2206a2968aaSIlan Elias 2216a2968aaSIlan Elias cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; 2226a2968aaSIlan Elias 2236a2968aaSIlan Elias nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, 2246a2968aaSIlan Elias sizeof(struct nci_rf_deactivate_cmd), 2256a2968aaSIlan Elias &cmd); 2266a2968aaSIlan Elias } 2276a2968aaSIlan Elias 2286a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev) 2296a2968aaSIlan Elias { 2306a2968aaSIlan Elias int rc = 0; 2316a2968aaSIlan Elias 2326a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 2336a2968aaSIlan Elias 2346a2968aaSIlan Elias if (test_bit(NCI_UP, &ndev->flags)) { 2356a2968aaSIlan Elias rc = -EALREADY; 2366a2968aaSIlan Elias goto done; 2376a2968aaSIlan Elias } 2386a2968aaSIlan Elias 2396a2968aaSIlan Elias if (ndev->ops->open(ndev)) { 2406a2968aaSIlan Elias rc = -EIO; 2416a2968aaSIlan Elias goto done; 2426a2968aaSIlan Elias } 2436a2968aaSIlan Elias 2446a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 2456a2968aaSIlan Elias 2466a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 2476a2968aaSIlan Elias 2486a2968aaSIlan Elias rc = __nci_request(ndev, nci_reset_req, 0, 2496a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 2506a2968aaSIlan Elias 2516a2968aaSIlan Elias if (!rc) { 2526a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_req, 0, 2536a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 2546a2968aaSIlan Elias } 2556a2968aaSIlan Elias 2566a2968aaSIlan Elias if (!rc) { 2576a2968aaSIlan Elias rc = __nci_request(ndev, nci_init_complete_req, 0, 2586a2968aaSIlan Elias msecs_to_jiffies(NCI_INIT_TIMEOUT)); 2596a2968aaSIlan Elias } 2606a2968aaSIlan Elias 2616a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 2626a2968aaSIlan Elias 2636a2968aaSIlan Elias if (!rc) { 2646a2968aaSIlan Elias set_bit(NCI_UP, &ndev->flags); 2656a2968aaSIlan Elias } else { 2666a2968aaSIlan Elias /* Init failed, cleanup */ 2676a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 2686a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 2696a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 2706a2968aaSIlan Elias 2716a2968aaSIlan Elias ndev->ops->close(ndev); 2726a2968aaSIlan Elias ndev->flags = 0; 2736a2968aaSIlan Elias } 2746a2968aaSIlan Elias 2756a2968aaSIlan Elias done: 2766a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 2776a2968aaSIlan Elias return rc; 2786a2968aaSIlan Elias } 2796a2968aaSIlan Elias 2806a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev) 2816a2968aaSIlan Elias { 2826a2968aaSIlan Elias nci_req_cancel(ndev, ENODEV); 2836a2968aaSIlan Elias mutex_lock(&ndev->req_lock); 2846a2968aaSIlan Elias 2856a2968aaSIlan Elias if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { 2866a2968aaSIlan Elias del_timer_sync(&ndev->cmd_timer); 2876a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 2886a2968aaSIlan Elias return 0; 2896a2968aaSIlan Elias } 2906a2968aaSIlan Elias 2916a2968aaSIlan Elias /* Drop RX and TX queues */ 2926a2968aaSIlan Elias skb_queue_purge(&ndev->rx_q); 2936a2968aaSIlan Elias skb_queue_purge(&ndev->tx_q); 2946a2968aaSIlan Elias 2956a2968aaSIlan Elias /* Flush RX and TX wq */ 2966a2968aaSIlan Elias flush_workqueue(ndev->rx_wq); 2976a2968aaSIlan Elias flush_workqueue(ndev->tx_wq); 2986a2968aaSIlan Elias 2996a2968aaSIlan Elias /* Reset device */ 3006a2968aaSIlan Elias skb_queue_purge(&ndev->cmd_q); 3016a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3026a2968aaSIlan Elias 3036a2968aaSIlan Elias set_bit(NCI_INIT, &ndev->flags); 3046a2968aaSIlan Elias __nci_request(ndev, nci_reset_req, 0, 3056a2968aaSIlan Elias msecs_to_jiffies(NCI_RESET_TIMEOUT)); 3066a2968aaSIlan Elias clear_bit(NCI_INIT, &ndev->flags); 3076a2968aaSIlan Elias 3086a2968aaSIlan Elias /* Flush cmd wq */ 3096a2968aaSIlan Elias flush_workqueue(ndev->cmd_wq); 3106a2968aaSIlan Elias 3116a2968aaSIlan Elias /* After this point our queues are empty 3126a2968aaSIlan Elias * and no works are scheduled. */ 3136a2968aaSIlan Elias ndev->ops->close(ndev); 3146a2968aaSIlan Elias 3156a2968aaSIlan Elias /* Clear flags */ 3166a2968aaSIlan Elias ndev->flags = 0; 3176a2968aaSIlan Elias 3186a2968aaSIlan Elias mutex_unlock(&ndev->req_lock); 3196a2968aaSIlan Elias 3206a2968aaSIlan Elias return 0; 3216a2968aaSIlan Elias } 3226a2968aaSIlan Elias 3236a2968aaSIlan Elias /* NCI command timer function */ 3246a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg) 3256a2968aaSIlan Elias { 3266a2968aaSIlan Elias struct nci_dev *ndev = (void *) arg; 3276a2968aaSIlan Elias 32820c239c1SJoe Perches pr_debug("entry\n"); 3296a2968aaSIlan Elias 3306a2968aaSIlan Elias atomic_set(&ndev->cmd_cnt, 1); 3316a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 3326a2968aaSIlan Elias } 3336a2968aaSIlan Elias 3346a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev) 3356a2968aaSIlan Elias { 3366a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 3376a2968aaSIlan Elias 33820c239c1SJoe Perches pr_debug("entry\n"); 3396a2968aaSIlan Elias 3406a2968aaSIlan Elias return nci_open_device(ndev); 3416a2968aaSIlan Elias } 3426a2968aaSIlan Elias 3436a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev) 3446a2968aaSIlan Elias { 3456a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 3466a2968aaSIlan Elias 34720c239c1SJoe Perches pr_debug("entry\n"); 3486a2968aaSIlan Elias 3496a2968aaSIlan Elias return nci_close_device(ndev); 3506a2968aaSIlan Elias } 3516a2968aaSIlan Elias 3526a2968aaSIlan Elias static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) 3536a2968aaSIlan Elias { 3546a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 3556a2968aaSIlan Elias int rc; 3566a2968aaSIlan Elias 35720c239c1SJoe Perches pr_debug("entry\n"); 3586a2968aaSIlan Elias 3596a2968aaSIlan Elias if (test_bit(NCI_DISCOVERY, &ndev->flags)) { 360ed1e0ad8SJoe Perches pr_err("unable to start poll, since poll is already active\n"); 3616a2968aaSIlan Elias return -EBUSY; 3626a2968aaSIlan Elias } 3636a2968aaSIlan Elias 364de054799SIlan Elias if (ndev->target_active_prot) { 365ed1e0ad8SJoe Perches pr_err("there is an active target\n"); 366de054799SIlan Elias return -EBUSY; 367de054799SIlan Elias } 368de054799SIlan Elias 3696a2968aaSIlan Elias if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { 37020c239c1SJoe Perches pr_debug("target is active, implicitly deactivate...\n"); 3716a2968aaSIlan Elias 3726a2968aaSIlan Elias rc = nci_request(ndev, nci_rf_deactivate_req, 0, 3736a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 3746a2968aaSIlan Elias if (rc) 3756a2968aaSIlan Elias return -EBUSY; 3766a2968aaSIlan Elias } 3776a2968aaSIlan Elias 3786a2968aaSIlan Elias rc = nci_request(ndev, nci_rf_discover_req, protocols, 3796a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); 3806a2968aaSIlan Elias 3816a2968aaSIlan Elias if (!rc) 3826a2968aaSIlan Elias ndev->poll_prots = protocols; 3836a2968aaSIlan Elias 3846a2968aaSIlan Elias return rc; 3856a2968aaSIlan Elias } 3866a2968aaSIlan Elias 3876a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev) 3886a2968aaSIlan Elias { 3896a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 3906a2968aaSIlan Elias 39120c239c1SJoe Perches pr_debug("entry\n"); 3926a2968aaSIlan Elias 3936a2968aaSIlan Elias if (!test_bit(NCI_DISCOVERY, &ndev->flags)) { 394ed1e0ad8SJoe Perches pr_err("unable to stop poll, since poll is not active\n"); 3956a2968aaSIlan Elias return; 3966a2968aaSIlan Elias } 3976a2968aaSIlan Elias 3986a2968aaSIlan Elias nci_request(ndev, nci_rf_deactivate_req, 0, 3996a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 4006a2968aaSIlan Elias } 4016a2968aaSIlan Elias 4026a2968aaSIlan Elias static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, 4036a2968aaSIlan Elias __u32 protocol) 4046a2968aaSIlan Elias { 4056a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4066a2968aaSIlan Elias 40720c239c1SJoe Perches pr_debug("entry, target_idx %d, protocol 0x%x\n", target_idx, protocol); 4086a2968aaSIlan Elias 4096a2968aaSIlan Elias if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { 410ed1e0ad8SJoe Perches pr_err("there is no available target to activate\n"); 4116a2968aaSIlan Elias return -EINVAL; 4126a2968aaSIlan Elias } 4136a2968aaSIlan Elias 4146a2968aaSIlan Elias if (ndev->target_active_prot) { 415ed1e0ad8SJoe Perches pr_err("there is already an active target\n"); 4166a2968aaSIlan Elias return -EBUSY; 4176a2968aaSIlan Elias } 4186a2968aaSIlan Elias 4196a2968aaSIlan Elias if (!(ndev->target_available_prots & (1 << protocol))) { 420ed1e0ad8SJoe Perches pr_err("target does not support the requested protocol 0x%x\n", 4216a2968aaSIlan Elias protocol); 4226a2968aaSIlan Elias return -EINVAL; 4236a2968aaSIlan Elias } 4246a2968aaSIlan Elias 4256a2968aaSIlan Elias ndev->target_active_prot = protocol; 4266a2968aaSIlan Elias ndev->target_available_prots = 0; 4276a2968aaSIlan Elias 4286a2968aaSIlan Elias return 0; 4296a2968aaSIlan Elias } 4306a2968aaSIlan Elias 4316a2968aaSIlan Elias static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) 4326a2968aaSIlan Elias { 4336a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 4346a2968aaSIlan Elias 43520c239c1SJoe Perches pr_debug("entry, target_idx %d\n", target_idx); 4366a2968aaSIlan Elias 4376a2968aaSIlan Elias if (!ndev->target_active_prot) { 438ed1e0ad8SJoe Perches pr_err("unable to deactivate target, no active target\n"); 4396a2968aaSIlan Elias return; 4406a2968aaSIlan Elias } 4416a2968aaSIlan Elias 4426a2968aaSIlan Elias ndev->target_active_prot = 0; 4436a2968aaSIlan Elias 4446a2968aaSIlan Elias if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { 4456a2968aaSIlan Elias nci_request(ndev, nci_rf_deactivate_req, 0, 4466a2968aaSIlan Elias msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); 4476a2968aaSIlan Elias } 4486a2968aaSIlan Elias } 4496a2968aaSIlan Elias 4506a2968aaSIlan Elias static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, 4516a2968aaSIlan Elias struct sk_buff *skb, 4526a2968aaSIlan Elias data_exchange_cb_t cb, 4536a2968aaSIlan Elias void *cb_context) 4546a2968aaSIlan Elias { 4556a2968aaSIlan Elias struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); 45638f04c6bSIlan Elias int rc; 4576a2968aaSIlan Elias 45820c239c1SJoe Perches pr_debug("entry, target_idx %d, len %d\n", target_idx, skb->len); 4596a2968aaSIlan Elias 4606a2968aaSIlan Elias if (!ndev->target_active_prot) { 461ed1e0ad8SJoe Perches pr_err("unable to exchange data, no active target\n"); 4626a2968aaSIlan Elias return -EINVAL; 4636a2968aaSIlan Elias } 4646a2968aaSIlan Elias 46538f04c6bSIlan Elias if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) 46638f04c6bSIlan Elias return -EBUSY; 46738f04c6bSIlan Elias 4686a2968aaSIlan Elias /* store cb and context to be used on receiving data */ 4696a2968aaSIlan Elias ndev->data_exchange_cb = cb; 4706a2968aaSIlan Elias ndev->data_exchange_cb_context = cb_context; 4716a2968aaSIlan Elias 472e8c0dacdSIlan Elias rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb); 47338f04c6bSIlan Elias if (rc) 47438f04c6bSIlan Elias clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); 47538f04c6bSIlan Elias 47638f04c6bSIlan Elias return rc; 4776a2968aaSIlan Elias } 4786a2968aaSIlan Elias 4796a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = { 4806a2968aaSIlan Elias .dev_up = nci_dev_up, 4816a2968aaSIlan Elias .dev_down = nci_dev_down, 4826a2968aaSIlan Elias .start_poll = nci_start_poll, 4836a2968aaSIlan Elias .stop_poll = nci_stop_poll, 4846a2968aaSIlan Elias .activate_target = nci_activate_target, 4856a2968aaSIlan Elias .deactivate_target = nci_deactivate_target, 4866a2968aaSIlan Elias .data_exchange = nci_data_exchange, 4876a2968aaSIlan Elias }; 4886a2968aaSIlan Elias 4896a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */ 4906a2968aaSIlan Elias 4916a2968aaSIlan Elias /** 4926a2968aaSIlan Elias * nci_allocate_device - allocate a new nci device 4936a2968aaSIlan Elias * 4946a2968aaSIlan Elias * @ops: device operations 4956a2968aaSIlan Elias * @supported_protocols: NFC protocols supported by the device 4966a2968aaSIlan Elias */ 4976a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops, 4986a2968aaSIlan Elias __u32 supported_protocols, 4996a2968aaSIlan Elias int tx_headroom, 5006a2968aaSIlan Elias int tx_tailroom) 5016a2968aaSIlan Elias { 5028ebafde0SDan Carpenter struct nci_dev *ndev; 5036a2968aaSIlan Elias 50420c239c1SJoe Perches pr_debug("entry, supported_protocols 0x%x\n", supported_protocols); 5056a2968aaSIlan Elias 5066a2968aaSIlan Elias if (!ops->open || !ops->close || !ops->send) 5078ebafde0SDan Carpenter return NULL; 5086a2968aaSIlan Elias 5096a2968aaSIlan Elias if (!supported_protocols) 5108ebafde0SDan Carpenter return NULL; 5116a2968aaSIlan Elias 5126a2968aaSIlan Elias ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); 5136a2968aaSIlan Elias if (!ndev) 5148ebafde0SDan Carpenter return NULL; 5156a2968aaSIlan Elias 5166a2968aaSIlan Elias ndev->ops = ops; 5176a2968aaSIlan Elias ndev->tx_headroom = tx_headroom; 5186a2968aaSIlan Elias ndev->tx_tailroom = tx_tailroom; 5196a2968aaSIlan Elias 5206a2968aaSIlan Elias ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, 5216a2968aaSIlan Elias supported_protocols, 5226a2968aaSIlan Elias tx_headroom + NCI_DATA_HDR_SIZE, 5236a2968aaSIlan Elias tx_tailroom); 5246a2968aaSIlan Elias if (!ndev->nfc_dev) 5256a2968aaSIlan Elias goto free_exit; 5266a2968aaSIlan Elias 5276a2968aaSIlan Elias nfc_set_drvdata(ndev->nfc_dev, ndev); 5286a2968aaSIlan Elias 5298ebafde0SDan Carpenter return ndev; 5306a2968aaSIlan Elias 5316a2968aaSIlan Elias free_exit: 5326a2968aaSIlan Elias kfree(ndev); 5338ebafde0SDan Carpenter return NULL; 5346a2968aaSIlan Elias } 5356a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device); 5366a2968aaSIlan Elias 5376a2968aaSIlan Elias /** 5386a2968aaSIlan Elias * nci_free_device - deallocate nci device 5396a2968aaSIlan Elias * 5406a2968aaSIlan Elias * @ndev: The nci device to deallocate 5416a2968aaSIlan Elias */ 5426a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev) 5436a2968aaSIlan Elias { 54420c239c1SJoe Perches pr_debug("entry\n"); 5456a2968aaSIlan Elias 5466a2968aaSIlan Elias nfc_free_device(ndev->nfc_dev); 5476a2968aaSIlan Elias kfree(ndev); 5486a2968aaSIlan Elias } 5496a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device); 5506a2968aaSIlan Elias 5516a2968aaSIlan Elias /** 5526a2968aaSIlan Elias * nci_register_device - register a nci device in the nfc subsystem 5536a2968aaSIlan Elias * 5546a2968aaSIlan Elias * @dev: The nci device to register 5556a2968aaSIlan Elias */ 5566a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev) 5576a2968aaSIlan Elias { 5586a2968aaSIlan Elias int rc; 5596a2968aaSIlan Elias struct device *dev = &ndev->nfc_dev->dev; 5606a2968aaSIlan Elias char name[32]; 5616a2968aaSIlan Elias 56220c239c1SJoe Perches pr_debug("entry\n"); 5636a2968aaSIlan Elias 5646a2968aaSIlan Elias rc = nfc_register_device(ndev->nfc_dev); 5656a2968aaSIlan Elias if (rc) 5666a2968aaSIlan Elias goto exit; 5676a2968aaSIlan Elias 5686a2968aaSIlan Elias ndev->flags = 0; 5696a2968aaSIlan Elias 5706a2968aaSIlan Elias INIT_WORK(&ndev->cmd_work, nci_cmd_work); 5716a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); 5726a2968aaSIlan Elias ndev->cmd_wq = create_singlethread_workqueue(name); 5736a2968aaSIlan Elias if (!ndev->cmd_wq) { 5746a2968aaSIlan Elias rc = -ENOMEM; 5756a2968aaSIlan Elias goto unreg_exit; 5766a2968aaSIlan Elias } 5776a2968aaSIlan Elias 5786a2968aaSIlan Elias INIT_WORK(&ndev->rx_work, nci_rx_work); 5796a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); 5806a2968aaSIlan Elias ndev->rx_wq = create_singlethread_workqueue(name); 5816a2968aaSIlan Elias if (!ndev->rx_wq) { 5826a2968aaSIlan Elias rc = -ENOMEM; 5836a2968aaSIlan Elias goto destroy_cmd_wq_exit; 5846a2968aaSIlan Elias } 5856a2968aaSIlan Elias 5866a2968aaSIlan Elias INIT_WORK(&ndev->tx_work, nci_tx_work); 5876a2968aaSIlan Elias snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); 5886a2968aaSIlan Elias ndev->tx_wq = create_singlethread_workqueue(name); 5896a2968aaSIlan Elias if (!ndev->tx_wq) { 5906a2968aaSIlan Elias rc = -ENOMEM; 5916a2968aaSIlan Elias goto destroy_rx_wq_exit; 5926a2968aaSIlan Elias } 5936a2968aaSIlan Elias 5946a2968aaSIlan Elias skb_queue_head_init(&ndev->cmd_q); 5956a2968aaSIlan Elias skb_queue_head_init(&ndev->rx_q); 5966a2968aaSIlan Elias skb_queue_head_init(&ndev->tx_q); 5976a2968aaSIlan Elias 5986a2968aaSIlan Elias setup_timer(&ndev->cmd_timer, nci_cmd_timer, 5996a2968aaSIlan Elias (unsigned long) ndev); 6006a2968aaSIlan Elias 6016a2968aaSIlan Elias mutex_init(&ndev->req_lock); 6026a2968aaSIlan Elias 6036a2968aaSIlan Elias goto exit; 6046a2968aaSIlan Elias 6056a2968aaSIlan Elias destroy_rx_wq_exit: 6066a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 6076a2968aaSIlan Elias 6086a2968aaSIlan Elias destroy_cmd_wq_exit: 6096a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 6106a2968aaSIlan Elias 6116a2968aaSIlan Elias unreg_exit: 6126a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 6136a2968aaSIlan Elias 6146a2968aaSIlan Elias exit: 6156a2968aaSIlan Elias return rc; 6166a2968aaSIlan Elias } 6176a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device); 6186a2968aaSIlan Elias 6196a2968aaSIlan Elias /** 6206a2968aaSIlan Elias * nci_unregister_device - unregister a nci device in the nfc subsystem 6216a2968aaSIlan Elias * 6226a2968aaSIlan Elias * @dev: The nci device to unregister 6236a2968aaSIlan Elias */ 6246a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev) 6256a2968aaSIlan Elias { 62620c239c1SJoe Perches pr_debug("entry\n"); 6276a2968aaSIlan Elias 6286a2968aaSIlan Elias nci_close_device(ndev); 6296a2968aaSIlan Elias 6306a2968aaSIlan Elias destroy_workqueue(ndev->cmd_wq); 6316a2968aaSIlan Elias destroy_workqueue(ndev->rx_wq); 6326a2968aaSIlan Elias destroy_workqueue(ndev->tx_wq); 6336a2968aaSIlan Elias 6346a2968aaSIlan Elias nfc_unregister_device(ndev->nfc_dev); 6356a2968aaSIlan Elias } 6366a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device); 6376a2968aaSIlan Elias 6386a2968aaSIlan Elias /** 6396a2968aaSIlan Elias * nci_recv_frame - receive frame from NCI drivers 6406a2968aaSIlan Elias * 6416a2968aaSIlan Elias * @skb: The sk_buff to receive 6426a2968aaSIlan Elias */ 6436a2968aaSIlan Elias int nci_recv_frame(struct sk_buff *skb) 6446a2968aaSIlan Elias { 6456a2968aaSIlan Elias struct nci_dev *ndev = (struct nci_dev *) skb->dev; 6466a2968aaSIlan Elias 64720c239c1SJoe Perches pr_debug("entry, len %d\n", skb->len); 6486a2968aaSIlan Elias 6496a2968aaSIlan Elias if (!ndev || (!test_bit(NCI_UP, &ndev->flags) 6506a2968aaSIlan Elias && !test_bit(NCI_INIT, &ndev->flags))) { 6516a2968aaSIlan Elias kfree_skb(skb); 6526a2968aaSIlan Elias return -ENXIO; 6536a2968aaSIlan Elias } 6546a2968aaSIlan Elias 6556a2968aaSIlan Elias /* Queue frame for rx worker thread */ 6566a2968aaSIlan Elias skb_queue_tail(&ndev->rx_q, skb); 6576a2968aaSIlan Elias queue_work(ndev->rx_wq, &ndev->rx_work); 6586a2968aaSIlan Elias 6596a2968aaSIlan Elias return 0; 6606a2968aaSIlan Elias } 6616a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame); 6626a2968aaSIlan Elias 6636a2968aaSIlan Elias static int nci_send_frame(struct sk_buff *skb) 6646a2968aaSIlan Elias { 6656a2968aaSIlan Elias struct nci_dev *ndev = (struct nci_dev *) skb->dev; 6666a2968aaSIlan Elias 66720c239c1SJoe Perches pr_debug("entry, len %d\n", skb->len); 6686a2968aaSIlan Elias 6696a2968aaSIlan Elias if (!ndev) { 6706a2968aaSIlan Elias kfree_skb(skb); 6716a2968aaSIlan Elias return -ENODEV; 6726a2968aaSIlan Elias } 6736a2968aaSIlan Elias 6746a2968aaSIlan Elias /* Get rid of skb owner, prior to sending to the driver. */ 6756a2968aaSIlan Elias skb_orphan(skb); 6766a2968aaSIlan Elias 6776a2968aaSIlan Elias return ndev->ops->send(skb); 6786a2968aaSIlan Elias } 6796a2968aaSIlan Elias 6806a2968aaSIlan Elias /* Send NCI command */ 6816a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) 6826a2968aaSIlan Elias { 6836a2968aaSIlan Elias struct nci_ctrl_hdr *hdr; 6846a2968aaSIlan Elias struct sk_buff *skb; 6856a2968aaSIlan Elias 68620c239c1SJoe Perches pr_debug("entry, opcode 0x%x, plen %d\n", opcode, plen); 6876a2968aaSIlan Elias 6886a2968aaSIlan Elias skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); 6896a2968aaSIlan Elias if (!skb) { 690ed1e0ad8SJoe Perches pr_err("no memory for command\n"); 6916a2968aaSIlan Elias return -ENOMEM; 6926a2968aaSIlan Elias } 6936a2968aaSIlan Elias 6946a2968aaSIlan Elias hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); 6956a2968aaSIlan Elias hdr->gid = nci_opcode_gid(opcode); 6966a2968aaSIlan Elias hdr->oid = nci_opcode_oid(opcode); 6976a2968aaSIlan Elias hdr->plen = plen; 6986a2968aaSIlan Elias 6996a2968aaSIlan Elias nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); 7006a2968aaSIlan Elias nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); 7016a2968aaSIlan Elias 7026a2968aaSIlan Elias if (plen) 7036a2968aaSIlan Elias memcpy(skb_put(skb, plen), payload, plen); 7046a2968aaSIlan Elias 7056a2968aaSIlan Elias skb->dev = (void *) ndev; 7066a2968aaSIlan Elias 7076a2968aaSIlan Elias skb_queue_tail(&ndev->cmd_q, skb); 7086a2968aaSIlan Elias queue_work(ndev->cmd_wq, &ndev->cmd_work); 7096a2968aaSIlan Elias 7106a2968aaSIlan Elias return 0; 7116a2968aaSIlan Elias } 7126a2968aaSIlan Elias 7136a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */ 7146a2968aaSIlan Elias 7156a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work) 7166a2968aaSIlan Elias { 7176a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); 7186a2968aaSIlan Elias struct sk_buff *skb; 7196a2968aaSIlan Elias 72020c239c1SJoe Perches pr_debug("entry, credits_cnt %d\n", atomic_read(&ndev->credits_cnt)); 7216a2968aaSIlan Elias 7226a2968aaSIlan Elias /* Send queued tx data */ 7236a2968aaSIlan Elias while (atomic_read(&ndev->credits_cnt)) { 7246a2968aaSIlan Elias skb = skb_dequeue(&ndev->tx_q); 7256a2968aaSIlan Elias if (!skb) 7266a2968aaSIlan Elias return; 7276a2968aaSIlan Elias 728db98c829SIlan Elias /* Check if data flow control is used */ 729db98c829SIlan Elias if (atomic_read(&ndev->credits_cnt) != 730db98c829SIlan Elias NCI_DATA_FLOW_CONTROL_NOT_USED) 7316a2968aaSIlan Elias atomic_dec(&ndev->credits_cnt); 7326a2968aaSIlan Elias 73320c239c1SJoe Perches pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n", 7346a2968aaSIlan Elias nci_pbf(skb->data), 7356a2968aaSIlan Elias nci_conn_id(skb->data), 7366a2968aaSIlan Elias nci_plen(skb->data)); 7376a2968aaSIlan Elias 7386a2968aaSIlan Elias nci_send_frame(skb); 7396a2968aaSIlan Elias } 7406a2968aaSIlan Elias } 7416a2968aaSIlan Elias 7426a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */ 7436a2968aaSIlan Elias 7446a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work) 7456a2968aaSIlan Elias { 7466a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); 7476a2968aaSIlan Elias struct sk_buff *skb; 7486a2968aaSIlan Elias 7496a2968aaSIlan Elias while ((skb = skb_dequeue(&ndev->rx_q))) { 7506a2968aaSIlan Elias /* Process frame */ 7516a2968aaSIlan Elias switch (nci_mt(skb->data)) { 7526a2968aaSIlan Elias case NCI_MT_RSP_PKT: 7536a2968aaSIlan Elias nci_rsp_packet(ndev, skb); 7546a2968aaSIlan Elias break; 7556a2968aaSIlan Elias 7566a2968aaSIlan Elias case NCI_MT_NTF_PKT: 7576a2968aaSIlan Elias nci_ntf_packet(ndev, skb); 7586a2968aaSIlan Elias break; 7596a2968aaSIlan Elias 7606a2968aaSIlan Elias case NCI_MT_DATA_PKT: 7616a2968aaSIlan Elias nci_rx_data_packet(ndev, skb); 7626a2968aaSIlan Elias break; 7636a2968aaSIlan Elias 7646a2968aaSIlan Elias default: 765ed1e0ad8SJoe Perches pr_err("unknown MT 0x%x\n", nci_mt(skb->data)); 7666a2968aaSIlan Elias kfree_skb(skb); 7676a2968aaSIlan Elias break; 7686a2968aaSIlan Elias } 7696a2968aaSIlan Elias } 7706a2968aaSIlan Elias } 7716a2968aaSIlan Elias 7726a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */ 7736a2968aaSIlan Elias 7746a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work) 7756a2968aaSIlan Elias { 7766a2968aaSIlan Elias struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); 7776a2968aaSIlan Elias struct sk_buff *skb; 7786a2968aaSIlan Elias 77920c239c1SJoe Perches pr_debug("entry, cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt)); 7806a2968aaSIlan Elias 7816a2968aaSIlan Elias /* Send queued command */ 7826a2968aaSIlan Elias if (atomic_read(&ndev->cmd_cnt)) { 7836a2968aaSIlan Elias skb = skb_dequeue(&ndev->cmd_q); 7846a2968aaSIlan Elias if (!skb) 7856a2968aaSIlan Elias return; 7866a2968aaSIlan Elias 7876a2968aaSIlan Elias atomic_dec(&ndev->cmd_cnt); 7886a2968aaSIlan Elias 78920c239c1SJoe Perches pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n", 7906a2968aaSIlan Elias nci_pbf(skb->data), 7916a2968aaSIlan Elias nci_opcode_gid(nci_opcode(skb->data)), 7926a2968aaSIlan Elias nci_opcode_oid(nci_opcode(skb->data)), 7936a2968aaSIlan Elias nci_plen(skb->data)); 7946a2968aaSIlan Elias 7956a2968aaSIlan Elias nci_send_frame(skb); 7966a2968aaSIlan Elias 7976a2968aaSIlan Elias mod_timer(&ndev->cmd_timer, 7986a2968aaSIlan Elias jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); 7996a2968aaSIlan Elias } 8006a2968aaSIlan Elias } 801