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