xref: /openbmc/linux/net/nfc/nci/core.c (revision 4df864c1)
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 
679b8d1a4cSChristophe Ricard int nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type,
689b8d1a4cSChristophe Ricard 					  struct dest_spec_params *params)
6985b9ce9aSRobert Dolca {
7085b9ce9aSRobert Dolca 	struct nci_conn_info *conn_info;
7185b9ce9aSRobert Dolca 
7285b9ce9aSRobert Dolca 	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
739b8d1a4cSChristophe Ricard 		if (conn_info->dest_type == dest_type) {
749b8d1a4cSChristophe Ricard 			if (!params)
7585b9ce9aSRobert Dolca 				return conn_info->conn_id;
769b8d1a4cSChristophe Ricard 			if (conn_info) {
779b8d1a4cSChristophe Ricard 				if (params->id == conn_info->dest_params->id &&
789b8d1a4cSChristophe Ricard 				    params->protocol == conn_info->dest_params->protocol)
799b8d1a4cSChristophe Ricard 					return conn_info->conn_id;
809b8d1a4cSChristophe Ricard 			}
819b8d1a4cSChristophe Ricard 		}
8285b9ce9aSRobert Dolca 	}
8385b9ce9aSRobert Dolca 
8485b9ce9aSRobert Dolca 	return -EINVAL;
8585b9ce9aSRobert Dolca }
869b8d1a4cSChristophe Ricard EXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params);
8785b9ce9aSRobert Dolca 
886a2968aaSIlan Elias /* ---- NCI requests ---- */
896a2968aaSIlan Elias 
906a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result)
916a2968aaSIlan Elias {
926a2968aaSIlan Elias 	if (ndev->req_status == NCI_REQ_PEND) {
936a2968aaSIlan Elias 		ndev->req_result = result;
946a2968aaSIlan Elias 		ndev->req_status = NCI_REQ_DONE;
956a2968aaSIlan Elias 		complete(&ndev->req_completion);
966a2968aaSIlan Elias 	}
976a2968aaSIlan Elias }
982df7f8c6SSamuel Ortiz EXPORT_SYMBOL(nci_req_complete);
996a2968aaSIlan Elias 
1006a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err)
1016a2968aaSIlan Elias {
1026a2968aaSIlan Elias 	if (ndev->req_status == NCI_REQ_PEND) {
1036a2968aaSIlan Elias 		ndev->req_result = err;
1046a2968aaSIlan Elias 		ndev->req_status = NCI_REQ_CANCELED;
1056a2968aaSIlan Elias 		complete(&ndev->req_completion);
1066a2968aaSIlan Elias 	}
1076a2968aaSIlan Elias }
1086a2968aaSIlan Elias 
1096a2968aaSIlan Elias /* Execute request and wait for completion. */
1106a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev,
1116a2968aaSIlan Elias 			 void (*req)(struct nci_dev *ndev, unsigned long opt),
112eb9bc6e9SSamuel Ortiz 			 unsigned long opt, __u32 timeout)
1136a2968aaSIlan Elias {
1146a2968aaSIlan Elias 	int rc = 0;
115f8c141c3SDan Carpenter 	long completion_rc;
1166a2968aaSIlan Elias 
1176a2968aaSIlan Elias 	ndev->req_status = NCI_REQ_PEND;
1186a2968aaSIlan Elias 
1199bec44bfSAxel Lin 	reinit_completion(&ndev->req_completion);
1206a2968aaSIlan Elias 	req(ndev, opt);
121eb9bc6e9SSamuel Ortiz 	completion_rc =
122eb9bc6e9SSamuel Ortiz 		wait_for_completion_interruptible_timeout(&ndev->req_completion,
1236a2968aaSIlan Elias 							  timeout);
1246a2968aaSIlan Elias 
12520c239c1SJoe Perches 	pr_debug("wait_for_completion return %ld\n", completion_rc);
1266a2968aaSIlan Elias 
1276a2968aaSIlan Elias 	if (completion_rc > 0) {
1286a2968aaSIlan Elias 		switch (ndev->req_status) {
1296a2968aaSIlan Elias 		case NCI_REQ_DONE:
1306a2968aaSIlan Elias 			rc = nci_to_errno(ndev->req_result);
1316a2968aaSIlan Elias 			break;
1326a2968aaSIlan Elias 
1336a2968aaSIlan Elias 		case NCI_REQ_CANCELED:
1346a2968aaSIlan Elias 			rc = -ndev->req_result;
1356a2968aaSIlan Elias 			break;
1366a2968aaSIlan Elias 
1376a2968aaSIlan Elias 		default:
1386a2968aaSIlan Elias 			rc = -ETIMEDOUT;
1396a2968aaSIlan Elias 			break;
1406a2968aaSIlan Elias 		}
1416a2968aaSIlan Elias 	} else {
142ed1e0ad8SJoe Perches 		pr_err("wait_for_completion_interruptible_timeout failed %ld\n",
1436a2968aaSIlan Elias 		       completion_rc);
1446a2968aaSIlan Elias 
1456a2968aaSIlan Elias 		rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc));
1466a2968aaSIlan Elias 	}
1476a2968aaSIlan Elias 
1486a2968aaSIlan Elias 	ndev->req_status = ndev->req_result = 0;
1496a2968aaSIlan Elias 
1506a2968aaSIlan Elias 	return rc;
1516a2968aaSIlan Elias }
1526a2968aaSIlan Elias 
15311f54f22SChristophe Ricard inline int nci_request(struct nci_dev *ndev,
154eb9bc6e9SSamuel Ortiz 		       void (*req)(struct nci_dev *ndev,
155eb9bc6e9SSamuel Ortiz 				   unsigned long opt),
1566a2968aaSIlan Elias 		       unsigned long opt, __u32 timeout)
1576a2968aaSIlan Elias {
1586a2968aaSIlan Elias 	int rc;
1596a2968aaSIlan Elias 
1606a2968aaSIlan Elias 	if (!test_bit(NCI_UP, &ndev->flags))
1616a2968aaSIlan Elias 		return -ENETDOWN;
1626a2968aaSIlan Elias 
1636a2968aaSIlan Elias 	/* Serialize all requests */
1646a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
1656a2968aaSIlan Elias 	rc = __nci_request(ndev, req, opt, timeout);
1666a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
1676a2968aaSIlan Elias 
1686a2968aaSIlan Elias 	return rc;
1696a2968aaSIlan Elias }
1706a2968aaSIlan Elias 
1716a2968aaSIlan Elias static void nci_reset_req(struct nci_dev *ndev, unsigned long opt)
1726a2968aaSIlan Elias {
173e8c0dacdSIlan Elias 	struct nci_core_reset_cmd cmd;
174e8c0dacdSIlan Elias 
175e8c0dacdSIlan Elias 	cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG;
176e8c0dacdSIlan Elias 	nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd);
1776a2968aaSIlan Elias }
1786a2968aaSIlan Elias 
1796a2968aaSIlan Elias static void nci_init_req(struct nci_dev *ndev, unsigned long opt)
1806a2968aaSIlan Elias {
1816a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL);
1826a2968aaSIlan Elias }
1836a2968aaSIlan Elias 
1846a2968aaSIlan Elias static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt)
1856a2968aaSIlan Elias {
1862eb1dc10SIlan Elias 	struct nci_rf_disc_map_cmd cmd;
1872eb1dc10SIlan Elias 	struct disc_map_config *cfg = cmd.mapping_configs;
1882eb1dc10SIlan Elias 	__u8 *num = &cmd.num_mapping_configs;
1896a2968aaSIlan Elias 	int i;
1906a2968aaSIlan Elias 
1916a2968aaSIlan Elias 	/* set rf mapping configurations */
1922eb1dc10SIlan Elias 	*num = 0;
1936a2968aaSIlan Elias 
1946a2968aaSIlan Elias 	/* by default mapping is set to NCI_RF_INTERFACE_FRAME */
1956a2968aaSIlan Elias 	for (i = 0; i < ndev->num_supported_rf_interfaces; i++) {
1966a2968aaSIlan Elias 		if (ndev->supported_rf_interfaces[i] ==
1976a2968aaSIlan Elias 		    NCI_RF_INTERFACE_ISO_DEP) {
1982eb1dc10SIlan Elias 			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
199637d85a7SIlan Elias 			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
200637d85a7SIlan Elias 				NCI_DISC_MAP_MODE_LISTEN;
201637d85a7SIlan Elias 			cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP;
2022eb1dc10SIlan Elias 			(*num)++;
2036a2968aaSIlan Elias 		} else if (ndev->supported_rf_interfaces[i] ==
2046a2968aaSIlan Elias 			   NCI_RF_INTERFACE_NFC_DEP) {
2052eb1dc10SIlan Elias 			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
206637d85a7SIlan Elias 			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
207637d85a7SIlan Elias 				NCI_DISC_MAP_MODE_LISTEN;
208637d85a7SIlan Elias 			cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP;
2092eb1dc10SIlan Elias 			(*num)++;
2106a2968aaSIlan Elias 		}
2116a2968aaSIlan Elias 
2122eb1dc10SIlan Elias 		if (*num == NCI_MAX_NUM_MAPPING_CONFIGS)
2136a2968aaSIlan Elias 			break;
2146a2968aaSIlan Elias 	}
2156a2968aaSIlan Elias 
2166a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD,
217eb9bc6e9SSamuel Ortiz 		     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
2186a2968aaSIlan Elias }
2196a2968aaSIlan Elias 
2207e035230SIlan Elias struct nci_set_config_param {
2217e035230SIlan Elias 	__u8	id;
2227e035230SIlan Elias 	size_t	len;
2237e035230SIlan Elias 	__u8	*val;
2247e035230SIlan Elias };
2257e035230SIlan Elias 
2267e035230SIlan Elias static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
2277e035230SIlan Elias {
2287e035230SIlan Elias 	struct nci_set_config_param *param = (struct nci_set_config_param *)opt;
2297e035230SIlan Elias 	struct nci_core_set_config_cmd cmd;
2307e035230SIlan Elias 
2317e035230SIlan Elias 	BUG_ON(param->len > NCI_MAX_PARAM_LEN);
2327e035230SIlan Elias 
2337e035230SIlan Elias 	cmd.num_params = 1;
2347e035230SIlan Elias 	cmd.param.id = param->id;
2357e035230SIlan Elias 	cmd.param.len = param->len;
2367e035230SIlan Elias 	memcpy(cmd.param.val, param->val, param->len);
2377e035230SIlan Elias 
2387e035230SIlan Elias 	nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
2397e035230SIlan Elias }
2407e035230SIlan Elias 
241772dccf4SJulien Lefrique struct nci_rf_discover_param {
242772dccf4SJulien Lefrique 	__u32	im_protocols;
243772dccf4SJulien Lefrique 	__u32	tm_protocols;
244772dccf4SJulien Lefrique };
245772dccf4SJulien Lefrique 
2466a2968aaSIlan Elias static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
2476a2968aaSIlan Elias {
248772dccf4SJulien Lefrique 	struct nci_rf_discover_param *param =
249772dccf4SJulien Lefrique 		(struct nci_rf_discover_param *)opt;
2506a2968aaSIlan Elias 	struct nci_rf_disc_cmd cmd;
2516a2968aaSIlan Elias 
2526a2968aaSIlan Elias 	cmd.num_disc_configs = 0;
2536a2968aaSIlan Elias 
2546a2968aaSIlan Elias 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
255772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
256772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_MIFARE_MASK ||
257772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_ISO14443_MASK ||
258772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
259637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
260637d85a7SIlan Elias 			NCI_NFC_A_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_ISO14443_B_MASK)) {
267637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
268637d85a7SIlan Elias 			NCI_NFC_B_PASSIVE_POLL_MODE;
2696a2968aaSIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
2706a2968aaSIlan Elias 		cmd.num_disc_configs++;
2716a2968aaSIlan Elias 	}
2726a2968aaSIlan Elias 
2736a2968aaSIlan Elias 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
274772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_FELICA_MASK ||
275772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
276637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
277637d85a7SIlan Elias 			NCI_NFC_F_PASSIVE_POLL_MODE;
2786a2968aaSIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
2796a2968aaSIlan Elias 		cmd.num_disc_configs++;
2806a2968aaSIlan Elias 	}
2816a2968aaSIlan Elias 
282cfdbeeafSVincent Cuissard 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
283772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
284cfdbeeafSVincent Cuissard 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
285cfdbeeafSVincent Cuissard 			NCI_NFC_V_PASSIVE_POLL_MODE;
286cfdbeeafSVincent Cuissard 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
287cfdbeeafSVincent Cuissard 		cmd.num_disc_configs++;
288cfdbeeafSVincent Cuissard 	}
289cfdbeeafSVincent Cuissard 
290772dccf4SJulien Lefrique 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
291772dccf4SJulien Lefrique 	    (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
292772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
293772dccf4SJulien Lefrique 			NCI_NFC_A_PASSIVE_LISTEN_MODE;
294772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
295772dccf4SJulien Lefrique 		cmd.num_disc_configs++;
296772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
297772dccf4SJulien Lefrique 			NCI_NFC_F_PASSIVE_LISTEN_MODE;
298772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
299772dccf4SJulien Lefrique 		cmd.num_disc_configs++;
300772dccf4SJulien Lefrique 	}
301772dccf4SJulien Lefrique 
3026a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
3036a2968aaSIlan Elias 		     (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
3046a2968aaSIlan Elias 		     &cmd);
3056a2968aaSIlan Elias }
3066a2968aaSIlan Elias 
307019c4fbaSIlan Elias struct nci_rf_discover_select_param {
308019c4fbaSIlan Elias 	__u8	rf_discovery_id;
309019c4fbaSIlan Elias 	__u8	rf_protocol;
310019c4fbaSIlan Elias };
311019c4fbaSIlan Elias 
312019c4fbaSIlan Elias static void nci_rf_discover_select_req(struct nci_dev *ndev, unsigned long opt)
313019c4fbaSIlan Elias {
314019c4fbaSIlan Elias 	struct nci_rf_discover_select_param *param =
315019c4fbaSIlan Elias 		(struct nci_rf_discover_select_param *)opt;
316019c4fbaSIlan Elias 	struct nci_rf_discover_select_cmd cmd;
317019c4fbaSIlan Elias 
318019c4fbaSIlan Elias 	cmd.rf_discovery_id = param->rf_discovery_id;
319019c4fbaSIlan Elias 	cmd.rf_protocol = param->rf_protocol;
320019c4fbaSIlan Elias 
321019c4fbaSIlan Elias 	switch (cmd.rf_protocol) {
322019c4fbaSIlan Elias 	case NCI_RF_PROTOCOL_ISO_DEP:
323019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP;
324019c4fbaSIlan Elias 		break;
325019c4fbaSIlan Elias 
326019c4fbaSIlan Elias 	case NCI_RF_PROTOCOL_NFC_DEP:
327019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP;
328019c4fbaSIlan Elias 		break;
329019c4fbaSIlan Elias 
330019c4fbaSIlan Elias 	default:
331019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_FRAME;
332019c4fbaSIlan Elias 		break;
333019c4fbaSIlan Elias 	}
334019c4fbaSIlan Elias 
335019c4fbaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD,
336eb9bc6e9SSamuel Ortiz 		     sizeof(struct nci_rf_discover_select_cmd), &cmd);
337019c4fbaSIlan Elias }
338019c4fbaSIlan Elias 
3396a2968aaSIlan Elias static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
3406a2968aaSIlan Elias {
3416a2968aaSIlan Elias 	struct nci_rf_deactivate_cmd cmd;
3426a2968aaSIlan Elias 
3439295b5b5SChristophe Ricard 	cmd.type = opt;
3446a2968aaSIlan Elias 
3456a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
346eb9bc6e9SSamuel Ortiz 		     sizeof(struct nci_rf_deactivate_cmd), &cmd);
3476a2968aaSIlan Elias }
3486a2968aaSIlan Elias 
3497bc4824eSRobert Dolca struct nci_cmd_param {
350759afb8dSChristophe Ricard 	__u16 opcode;
351759afb8dSChristophe Ricard 	size_t len;
352759afb8dSChristophe Ricard 	__u8 *payload;
353759afb8dSChristophe Ricard };
354759afb8dSChristophe Ricard 
3557bc4824eSRobert Dolca static void nci_generic_req(struct nci_dev *ndev, unsigned long opt)
356759afb8dSChristophe Ricard {
3577bc4824eSRobert Dolca 	struct nci_cmd_param *param =
3587bc4824eSRobert Dolca 		(struct nci_cmd_param *)opt;
359759afb8dSChristophe Ricard 
360759afb8dSChristophe Ricard 	nci_send_cmd(ndev, param->opcode, param->len, param->payload);
361759afb8dSChristophe Ricard }
362759afb8dSChristophe Ricard 
363759afb8dSChristophe Ricard int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, __u8 *payload)
364759afb8dSChristophe Ricard {
3657bc4824eSRobert Dolca 	struct nci_cmd_param param;
366759afb8dSChristophe Ricard 
367759afb8dSChristophe Ricard 	param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
368759afb8dSChristophe Ricard 	param.len = len;
369759afb8dSChristophe Ricard 	param.payload = payload;
370759afb8dSChristophe Ricard 
3717bc4824eSRobert Dolca 	return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
372759afb8dSChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
373759afb8dSChristophe Ricard }
374759afb8dSChristophe Ricard EXPORT_SYMBOL(nci_prop_cmd);
375759afb8dSChristophe Ricard 
3767bc4824eSRobert Dolca int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len, __u8 *payload)
3777bc4824eSRobert Dolca {
3787bc4824eSRobert Dolca 	struct nci_cmd_param param;
3797bc4824eSRobert Dolca 
3807bc4824eSRobert Dolca 	param.opcode = opcode;
3817bc4824eSRobert Dolca 	param.len = len;
3827bc4824eSRobert Dolca 	param.payload = payload;
3837bc4824eSRobert Dolca 
3847bc4824eSRobert Dolca 	return __nci_request(ndev, nci_generic_req, (unsigned long)&param,
3857bc4824eSRobert Dolca 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
3867bc4824eSRobert Dolca }
3877bc4824eSRobert Dolca EXPORT_SYMBOL(nci_core_cmd);
3887bc4824eSRobert Dolca 
389025a0cb8SRobert Baldyga int nci_core_reset(struct nci_dev *ndev)
390025a0cb8SRobert Baldyga {
391025a0cb8SRobert Baldyga 	return __nci_request(ndev, nci_reset_req, 0,
392025a0cb8SRobert Baldyga 			     msecs_to_jiffies(NCI_RESET_TIMEOUT));
393025a0cb8SRobert Baldyga }
394025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_reset);
395025a0cb8SRobert Baldyga 
396025a0cb8SRobert Baldyga int nci_core_init(struct nci_dev *ndev)
397025a0cb8SRobert Baldyga {
398025a0cb8SRobert Baldyga 	return __nci_request(ndev, nci_init_req, 0,
399025a0cb8SRobert Baldyga 			     msecs_to_jiffies(NCI_INIT_TIMEOUT));
400025a0cb8SRobert Baldyga }
401025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_init);
402025a0cb8SRobert Baldyga 
4031c53855fSChristophe Ricard struct nci_loopback_data {
4041c53855fSChristophe Ricard 	u8 conn_id;
4051c53855fSChristophe Ricard 	struct sk_buff *data;
4061c53855fSChristophe Ricard };
4071c53855fSChristophe Ricard 
4081c53855fSChristophe Ricard static void nci_send_data_req(struct nci_dev *ndev, unsigned long opt)
4091c53855fSChristophe Ricard {
4101c53855fSChristophe Ricard 	struct nci_loopback_data *data = (struct nci_loopback_data *)opt;
4111c53855fSChristophe Ricard 
4121c53855fSChristophe Ricard 	nci_send_data(ndev, data->conn_id, data->data);
4131c53855fSChristophe Ricard }
4141c53855fSChristophe Ricard 
4151c53855fSChristophe Ricard static void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err)
4161c53855fSChristophe Ricard {
4171c53855fSChristophe Ricard 	struct nci_dev *ndev = (struct nci_dev *)context;
4181c53855fSChristophe Ricard 	struct nci_conn_info    *conn_info;
4191c53855fSChristophe Ricard 
4201c53855fSChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
4211c53855fSChristophe Ricard 	if (!conn_info) {
4221c53855fSChristophe Ricard 		nci_req_complete(ndev, NCI_STATUS_REJECTED);
4231c53855fSChristophe Ricard 		return;
4241c53855fSChristophe Ricard 	}
4251c53855fSChristophe Ricard 
4261c53855fSChristophe Ricard 	conn_info->rx_skb = skb;
4271c53855fSChristophe Ricard 
4281c53855fSChristophe Ricard 	nci_req_complete(ndev, NCI_STATUS_OK);
4291c53855fSChristophe Ricard }
4301c53855fSChristophe Ricard 
4311c53855fSChristophe Ricard int nci_nfcc_loopback(struct nci_dev *ndev, void *data, size_t data_len,
4321c53855fSChristophe Ricard 		      struct sk_buff **resp)
4331c53855fSChristophe Ricard {
4341c53855fSChristophe Ricard 	int r;
4351c53855fSChristophe Ricard 	struct nci_loopback_data loopback_data;
4361c53855fSChristophe Ricard 	struct nci_conn_info *conn_info;
4371c53855fSChristophe Ricard 	struct sk_buff *skb;
4381c53855fSChristophe Ricard 	int conn_id = nci_get_conn_info_by_dest_type_params(ndev,
4391c53855fSChristophe Ricard 					NCI_DESTINATION_NFCC_LOOPBACK, NULL);
4401c53855fSChristophe Ricard 
4411c53855fSChristophe Ricard 	if (conn_id < 0) {
4421c53855fSChristophe Ricard 		r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK,
4431c53855fSChristophe Ricard 					 0, 0, NULL);
4441c53855fSChristophe Ricard 		if (r != NCI_STATUS_OK)
4451c53855fSChristophe Ricard 			return r;
4461c53855fSChristophe Ricard 
4471c53855fSChristophe Ricard 		conn_id = nci_get_conn_info_by_dest_type_params(ndev,
4481c53855fSChristophe Ricard 					NCI_DESTINATION_NFCC_LOOPBACK,
4491c53855fSChristophe Ricard 					NULL);
4501c53855fSChristophe Ricard 	}
4511c53855fSChristophe Ricard 
4521c53855fSChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
4531c53855fSChristophe Ricard 	if (!conn_info)
4541c53855fSChristophe Ricard 		return -EPROTO;
4551c53855fSChristophe Ricard 
4561c53855fSChristophe Ricard 	/* store cb and context to be used on receiving data */
4571c53855fSChristophe Ricard 	conn_info->data_exchange_cb = nci_nfcc_loopback_cb;
4581c53855fSChristophe Ricard 	conn_info->data_exchange_cb_context = ndev;
4591c53855fSChristophe Ricard 
4601c53855fSChristophe Ricard 	skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL);
4611c53855fSChristophe Ricard 	if (!skb)
4621c53855fSChristophe Ricard 		return -ENOMEM;
4631c53855fSChristophe Ricard 
4641c53855fSChristophe Ricard 	skb_reserve(skb, NCI_DATA_HDR_SIZE);
46559ae1d12SJohannes Berg 	skb_put_data(skb, data, data_len);
4661c53855fSChristophe Ricard 
4671c53855fSChristophe Ricard 	loopback_data.conn_id = conn_id;
4681c53855fSChristophe Ricard 	loopback_data.data = skb;
4691c53855fSChristophe Ricard 
4701c53855fSChristophe Ricard 	ndev->cur_conn_id = conn_id;
4711c53855fSChristophe Ricard 	r = nci_request(ndev, nci_send_data_req, (unsigned long)&loopback_data,
4721c53855fSChristophe Ricard 			msecs_to_jiffies(NCI_DATA_TIMEOUT));
4731c53855fSChristophe Ricard 	if (r == NCI_STATUS_OK && resp)
4741c53855fSChristophe Ricard 		*resp = conn_info->rx_skb;
4751c53855fSChristophe Ricard 
4761c53855fSChristophe Ricard 	return r;
4771c53855fSChristophe Ricard }
4781c53855fSChristophe Ricard EXPORT_SYMBOL(nci_nfcc_loopback);
4791c53855fSChristophe Ricard 
4806a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev)
4816a2968aaSIlan Elias {
4826a2968aaSIlan Elias 	int rc = 0;
4836a2968aaSIlan Elias 
4846a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
4856a2968aaSIlan Elias 
4866a2968aaSIlan Elias 	if (test_bit(NCI_UP, &ndev->flags)) {
4876a2968aaSIlan Elias 		rc = -EALREADY;
4886a2968aaSIlan Elias 		goto done;
4896a2968aaSIlan Elias 	}
4906a2968aaSIlan Elias 
4916a2968aaSIlan Elias 	if (ndev->ops->open(ndev)) {
4926a2968aaSIlan Elias 		rc = -EIO;
4936a2968aaSIlan Elias 		goto done;
4946a2968aaSIlan Elias 	}
4956a2968aaSIlan Elias 
4966a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
4976a2968aaSIlan Elias 
4986a2968aaSIlan Elias 	set_bit(NCI_INIT, &ndev->flags);
4996a2968aaSIlan Elias 
500c39daeeeSChristophe Ricard 	if (ndev->ops->init)
501c39daeeeSChristophe Ricard 		rc = ndev->ops->init(ndev);
502c39daeeeSChristophe Ricard 
503c39daeeeSChristophe Ricard 	if (!rc) {
5046a2968aaSIlan Elias 		rc = __nci_request(ndev, nci_reset_req, 0,
5056a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_RESET_TIMEOUT));
506c39daeeeSChristophe Ricard 	}
5076a2968aaSIlan Elias 
50881859ab8SChristophe Ricard 	if (!rc && ndev->ops->setup) {
50981859ab8SChristophe Ricard 		rc = ndev->ops->setup(ndev);
51081859ab8SChristophe Ricard 	}
51186e8586eSAmitkumar Karwar 
5126a2968aaSIlan Elias 	if (!rc) {
5136a2968aaSIlan Elias 		rc = __nci_request(ndev, nci_init_req, 0,
5146a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
5156a2968aaSIlan Elias 	}
5166a2968aaSIlan Elias 
517e4dbd625SRobert Dolca 	if (!rc && ndev->ops->post_setup)
518fdf79bd4SRobert Baldyga 		rc = ndev->ops->post_setup(ndev);
519fdf79bd4SRobert Baldyga 
5206a2968aaSIlan Elias 	if (!rc) {
5216a2968aaSIlan Elias 		rc = __nci_request(ndev, nci_init_complete_req, 0,
5226a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
5236a2968aaSIlan Elias 	}
5246a2968aaSIlan Elias 
5256a2968aaSIlan Elias 	clear_bit(NCI_INIT, &ndev->flags);
5266a2968aaSIlan Elias 
5276a2968aaSIlan Elias 	if (!rc) {
5286a2968aaSIlan Elias 		set_bit(NCI_UP, &ndev->flags);
529019c4fbaSIlan Elias 		nci_clear_target_list(ndev);
5308939e47fSIlan Elias 		atomic_set(&ndev->state, NCI_IDLE);
5316a2968aaSIlan Elias 	} else {
5326a2968aaSIlan Elias 		/* Init failed, cleanup */
5336a2968aaSIlan Elias 		skb_queue_purge(&ndev->cmd_q);
5346a2968aaSIlan Elias 		skb_queue_purge(&ndev->rx_q);
5356a2968aaSIlan Elias 		skb_queue_purge(&ndev->tx_q);
5366a2968aaSIlan Elias 
5376a2968aaSIlan Elias 		ndev->ops->close(ndev);
5386a2968aaSIlan Elias 		ndev->flags = 0;
5396a2968aaSIlan Elias 	}
5406a2968aaSIlan Elias 
5416a2968aaSIlan Elias done:
5426a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
5436a2968aaSIlan Elias 	return rc;
5446a2968aaSIlan Elias }
5456a2968aaSIlan Elias 
5466a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev)
5476a2968aaSIlan Elias {
5486a2968aaSIlan Elias 	nci_req_cancel(ndev, ENODEV);
5496a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
5506a2968aaSIlan Elias 
5516a2968aaSIlan Elias 	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
5526a2968aaSIlan Elias 		del_timer_sync(&ndev->cmd_timer);
553c4bf98b2SIlan Elias 		del_timer_sync(&ndev->data_timer);
5546a2968aaSIlan Elias 		mutex_unlock(&ndev->req_lock);
5556a2968aaSIlan Elias 		return 0;
5566a2968aaSIlan Elias 	}
5576a2968aaSIlan Elias 
5586a2968aaSIlan Elias 	/* Drop RX and TX queues */
5596a2968aaSIlan Elias 	skb_queue_purge(&ndev->rx_q);
5606a2968aaSIlan Elias 	skb_queue_purge(&ndev->tx_q);
5616a2968aaSIlan Elias 
5626a2968aaSIlan Elias 	/* Flush RX and TX wq */
5636a2968aaSIlan Elias 	flush_workqueue(ndev->rx_wq);
5646a2968aaSIlan Elias 	flush_workqueue(ndev->tx_wq);
5656a2968aaSIlan Elias 
5666a2968aaSIlan Elias 	/* Reset device */
5676a2968aaSIlan Elias 	skb_queue_purge(&ndev->cmd_q);
5686a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
5696a2968aaSIlan Elias 
5706a2968aaSIlan Elias 	set_bit(NCI_INIT, &ndev->flags);
5716a2968aaSIlan Elias 	__nci_request(ndev, nci_reset_req, 0,
5726a2968aaSIlan Elias 		      msecs_to_jiffies(NCI_RESET_TIMEOUT));
5730e70cba7SChristophe Ricard 
5740e70cba7SChristophe Ricard 	/* After this point our queues are empty
5750e70cba7SChristophe Ricard 	 * and no works are scheduled.
5760e70cba7SChristophe Ricard 	 */
5770e70cba7SChristophe Ricard 	ndev->ops->close(ndev);
5780e70cba7SChristophe Ricard 
5796a2968aaSIlan Elias 	clear_bit(NCI_INIT, &ndev->flags);
5806a2968aaSIlan Elias 
581fa9be5f0SAmitkumar Karwar 	del_timer_sync(&ndev->cmd_timer);
582fa9be5f0SAmitkumar Karwar 
5836a2968aaSIlan Elias 	/* Flush cmd wq */
5846a2968aaSIlan Elias 	flush_workqueue(ndev->cmd_wq);
5856a2968aaSIlan Elias 
5866a2968aaSIlan Elias 	/* Clear flags */
5876a2968aaSIlan Elias 	ndev->flags = 0;
5886a2968aaSIlan Elias 
5896a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
5906a2968aaSIlan Elias 
5916a2968aaSIlan Elias 	return 0;
5926a2968aaSIlan Elias }
5936a2968aaSIlan Elias 
5946a2968aaSIlan Elias /* NCI command timer function */
5956a2968aaSIlan Elias static void nci_cmd_timer(unsigned long arg)
5966a2968aaSIlan Elias {
5976a2968aaSIlan Elias 	struct nci_dev *ndev = (void *) arg;
5986a2968aaSIlan Elias 
5996a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
6006a2968aaSIlan Elias 	queue_work(ndev->cmd_wq, &ndev->cmd_work);
6016a2968aaSIlan Elias }
6026a2968aaSIlan Elias 
603c4bf98b2SIlan Elias /* NCI data exchange timer function */
604c4bf98b2SIlan Elias static void nci_data_timer(unsigned long arg)
605c4bf98b2SIlan Elias {
606c4bf98b2SIlan Elias 	struct nci_dev *ndev = (void *) arg;
607c4bf98b2SIlan Elias 
608c4bf98b2SIlan Elias 	set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
609c4bf98b2SIlan Elias 	queue_work(ndev->rx_wq, &ndev->rx_work);
610c4bf98b2SIlan Elias }
611c4bf98b2SIlan Elias 
6126a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev)
6136a2968aaSIlan Elias {
6146a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
6156a2968aaSIlan Elias 
6166a2968aaSIlan Elias 	return nci_open_device(ndev);
6176a2968aaSIlan Elias }
6186a2968aaSIlan Elias 
6196a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev)
6206a2968aaSIlan Elias {
6216a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
6226a2968aaSIlan Elias 
6236a2968aaSIlan Elias 	return nci_close_device(ndev);
6246a2968aaSIlan Elias }
6256a2968aaSIlan Elias 
62622c15bf3SAmitkumar Karwar int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, __u8 *val)
62722c15bf3SAmitkumar Karwar {
62822c15bf3SAmitkumar Karwar 	struct nci_set_config_param param;
62922c15bf3SAmitkumar Karwar 
63022c15bf3SAmitkumar Karwar 	if (!val || !len)
63122c15bf3SAmitkumar Karwar 		return 0;
63222c15bf3SAmitkumar Karwar 
63322c15bf3SAmitkumar Karwar 	param.id = id;
63422c15bf3SAmitkumar Karwar 	param.len = len;
63522c15bf3SAmitkumar Karwar 	param.val = val;
63622c15bf3SAmitkumar Karwar 
63722c15bf3SAmitkumar Karwar 	return __nci_request(ndev, nci_set_config_req, (unsigned long)&param,
63822c15bf3SAmitkumar Karwar 			     msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
63922c15bf3SAmitkumar Karwar }
64022c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config);
64122c15bf3SAmitkumar Karwar 
642af9c8aa6SChristophe Ricard static void nci_nfcee_discover_req(struct nci_dev *ndev, unsigned long opt)
643af9c8aa6SChristophe Ricard {
644af9c8aa6SChristophe Ricard 	struct nci_nfcee_discover_cmd cmd;
645af9c8aa6SChristophe Ricard 	__u8 action = opt;
646af9c8aa6SChristophe Ricard 
647af9c8aa6SChristophe Ricard 	cmd.discovery_action = action;
648af9c8aa6SChristophe Ricard 
649af9c8aa6SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd);
650af9c8aa6SChristophe Ricard }
651af9c8aa6SChristophe Ricard 
652af9c8aa6SChristophe Ricard int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
653af9c8aa6SChristophe Ricard {
65421d19f87SSamuel Ortiz 	return __nci_request(ndev, nci_nfcee_discover_req, action,
655af9c8aa6SChristophe Ricard 				msecs_to_jiffies(NCI_CMD_TIMEOUT));
656af9c8aa6SChristophe Ricard }
657af9c8aa6SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_discover);
658af9c8aa6SChristophe Ricard 
659f7f793f3SChristophe Ricard static void nci_nfcee_mode_set_req(struct nci_dev *ndev, unsigned long opt)
660f7f793f3SChristophe Ricard {
661f7f793f3SChristophe Ricard 	struct nci_nfcee_mode_set_cmd *cmd =
662f7f793f3SChristophe Ricard 					(struct nci_nfcee_mode_set_cmd *)opt;
663f7f793f3SChristophe Ricard 
664f7f793f3SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD,
665f7f793f3SChristophe Ricard 		     sizeof(struct nci_nfcee_mode_set_cmd), cmd);
666f7f793f3SChristophe Ricard }
667f7f793f3SChristophe Ricard 
668f7f793f3SChristophe Ricard int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
669f7f793f3SChristophe Ricard {
670f7f793f3SChristophe Ricard 	struct nci_nfcee_mode_set_cmd cmd;
671f7f793f3SChristophe Ricard 
672f7f793f3SChristophe Ricard 	cmd.nfcee_id = nfcee_id;
673f7f793f3SChristophe Ricard 	cmd.nfcee_mode = nfcee_mode;
674f7f793f3SChristophe Ricard 
67521d19f87SSamuel Ortiz 	return __nci_request(ndev, nci_nfcee_mode_set_req,
67621d19f87SSamuel Ortiz 			     (unsigned long)&cmd,
677f7f793f3SChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
678f7f793f3SChristophe Ricard }
679f7f793f3SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_mode_set);
680f7f793f3SChristophe Ricard 
681736bb957SChristophe Ricard static void nci_core_conn_create_req(struct nci_dev *ndev, unsigned long opt)
682736bb957SChristophe Ricard {
683b16ae716SChristophe Ricard 	struct core_conn_create_data *data =
684b16ae716SChristophe Ricard 					(struct core_conn_create_data *)opt;
685736bb957SChristophe Ricard 
686b16ae716SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd);
687736bb957SChristophe Ricard }
688736bb957SChristophe Ricard 
689b16ae716SChristophe Ricard int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
690b16ae716SChristophe Ricard 			 u8 number_destination_params,
691b16ae716SChristophe Ricard 			 size_t params_len,
692736bb957SChristophe Ricard 			 struct core_conn_create_dest_spec_params *params)
693736bb957SChristophe Ricard {
694b16ae716SChristophe Ricard 	int r;
695b16ae716SChristophe Ricard 	struct nci_core_conn_create_cmd *cmd;
696b16ae716SChristophe Ricard 	struct core_conn_create_data data;
697b16ae716SChristophe Ricard 
698b16ae716SChristophe Ricard 	data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
699b16ae716SChristophe Ricard 	cmd = kzalloc(data.length, GFP_KERNEL);
700b16ae716SChristophe Ricard 	if (!cmd)
701b16ae716SChristophe Ricard 		return -ENOMEM;
702b16ae716SChristophe Ricard 
703b16ae716SChristophe Ricard 	cmd->destination_type = destination_type;
704b16ae716SChristophe Ricard 	cmd->number_destination_params = number_destination_params;
705b16ae716SChristophe Ricard 
706b16ae716SChristophe Ricard 	data.cmd = cmd;
707caa575a8SRobert Dolca 
70818836029SChristophe Ricard 	if (params) {
70918836029SChristophe Ricard 		memcpy(cmd->params, params, params_len);
710caa575a8SRobert Dolca 		if (params->length > 0)
7119b8d1a4cSChristophe Ricard 			memcpy(&ndev->cur_params,
7129b8d1a4cSChristophe Ricard 			       &params->value[DEST_SPEC_PARAMS_ID_INDEX],
7139b8d1a4cSChristophe Ricard 			       sizeof(struct dest_spec_params));
714caa575a8SRobert Dolca 		else
7159b8d1a4cSChristophe Ricard 			ndev->cur_params.id = 0;
71618836029SChristophe Ricard 	} else {
7179b8d1a4cSChristophe Ricard 		ndev->cur_params.id = 0;
71818836029SChristophe Ricard 	}
7199b8d1a4cSChristophe Ricard 	ndev->cur_dest_type = destination_type;
720b16ae716SChristophe Ricard 
72118836029SChristophe Ricard 	r = __nci_request(ndev, nci_core_conn_create_req, (unsigned long)&data,
722736bb957SChristophe Ricard 			  msecs_to_jiffies(NCI_CMD_TIMEOUT));
723b16ae716SChristophe Ricard 	kfree(cmd);
724b16ae716SChristophe Ricard 	return r;
725736bb957SChristophe Ricard }
726736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_create);
727736bb957SChristophe Ricard 
728736bb957SChristophe Ricard static void nci_core_conn_close_req(struct nci_dev *ndev, unsigned long opt)
729736bb957SChristophe Ricard {
730736bb957SChristophe Ricard 	__u8 conn_id = opt;
731736bb957SChristophe Ricard 
732736bb957SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id);
733736bb957SChristophe Ricard }
734736bb957SChristophe Ricard 
735736bb957SChristophe Ricard int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
736736bb957SChristophe Ricard {
737de5ea851SChristophe Ricard 	ndev->cur_conn_id = conn_id;
73821d19f87SSamuel Ortiz 	return __nci_request(ndev, nci_core_conn_close_req, conn_id,
739736bb957SChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
740736bb957SChristophe Ricard }
741736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_close);
742736bb957SChristophe Ricard 
7437e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
7447e035230SIlan Elias {
7457e035230SIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
7467e035230SIlan Elias 	struct nci_set_config_param param;
747529ee066SJulien Lefrique 	int rc;
7487e035230SIlan Elias 
7497e035230SIlan Elias 	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
7507e035230SIlan Elias 	if ((param.val == NULL) || (param.len == 0))
751f9fc36f4SSzymon Janc 		return 0;
7527e035230SIlan Elias 
753460d8f97SSzymon Janc 	if (param.len > NFC_MAX_GT_LEN)
7547e035230SIlan Elias 		return -EINVAL;
7557e035230SIlan Elias 
7567e035230SIlan Elias 	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
7577e035230SIlan Elias 
758529ee066SJulien Lefrique 	rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
759529ee066SJulien Lefrique 			 msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
760529ee066SJulien Lefrique 	if (rc)
761529ee066SJulien Lefrique 		return rc;
762529ee066SJulien Lefrique 
763529ee066SJulien Lefrique 	param.id = NCI_LN_ATR_RES_GEN_BYTES;
764529ee066SJulien Lefrique 
765f9fc36f4SSzymon Janc 	return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
7667e035230SIlan Elias 			   msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
7677e035230SIlan Elias }
7687e035230SIlan Elias 
76990d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
77090d78c13SJulien Lefrique {
77190d78c13SJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
77290d78c13SJulien Lefrique 	int rc;
77390d78c13SJulien Lefrique 	__u8 val;
77490d78c13SJulien Lefrique 
77590d78c13SJulien Lefrique 	val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
77690d78c13SJulien Lefrique 
77790d78c13SJulien Lefrique 	rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
77890d78c13SJulien Lefrique 	if (rc)
77990d78c13SJulien Lefrique 		return rc;
78090d78c13SJulien Lefrique 
78190d78c13SJulien Lefrique 	val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
78290d78c13SJulien Lefrique 
78390d78c13SJulien Lefrique 	rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
78490d78c13SJulien Lefrique 	if (rc)
78590d78c13SJulien Lefrique 		return rc;
78690d78c13SJulien Lefrique 
78790d78c13SJulien Lefrique 	val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
78890d78c13SJulien Lefrique 
78990d78c13SJulien Lefrique 	return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
79090d78c13SJulien Lefrique }
79190d78c13SJulien Lefrique 
792fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev,
793fe7c5800SSamuel Ortiz 			  __u32 im_protocols, __u32 tm_protocols)
7946a2968aaSIlan Elias {
7956a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
796772dccf4SJulien Lefrique 	struct nci_rf_discover_param param;
7976a2968aaSIlan Elias 	int rc;
7986a2968aaSIlan Elias 
799019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
800019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) {
801ed1e0ad8SJoe Perches 		pr_err("unable to start poll, since poll is already active\n");
8026a2968aaSIlan Elias 		return -EBUSY;
8036a2968aaSIlan Elias 	}
8046a2968aaSIlan Elias 
805de054799SIlan Elias 	if (ndev->target_active_prot) {
806ed1e0ad8SJoe Perches 		pr_err("there is an active target\n");
807de054799SIlan Elias 		return -EBUSY;
808de054799SIlan Elias 	}
809de054799SIlan Elias 
810019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) ||
811019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
812019c4fbaSIlan Elias 		pr_debug("target active or w4 select, implicitly deactivate\n");
8136a2968aaSIlan Elias 
8149295b5b5SChristophe Ricard 		rc = nci_request(ndev, nci_rf_deactivate_req,
8159295b5b5SChristophe Ricard 				 NCI_DEACTIVATE_TYPE_IDLE_MODE,
8166a2968aaSIlan Elias 				 msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
8176a2968aaSIlan Elias 		if (rc)
8186a2968aaSIlan Elias 			return -EBUSY;
8196a2968aaSIlan Elias 	}
8206a2968aaSIlan Elias 
821529ee066SJulien Lefrique 	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
8227e035230SIlan Elias 		rc = nci_set_local_general_bytes(nfc_dev);
8237e035230SIlan Elias 		if (rc) {
8247e035230SIlan Elias 			pr_err("failed to set local general bytes\n");
8257e035230SIlan Elias 			return rc;
8267e035230SIlan Elias 		}
8277e035230SIlan Elias 	}
8287e035230SIlan Elias 
82990d78c13SJulien Lefrique 	if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
83090d78c13SJulien Lefrique 		rc = nci_set_listen_parameters(nfc_dev);
83190d78c13SJulien Lefrique 		if (rc)
83290d78c13SJulien Lefrique 			pr_err("failed to set listen parameters\n");
83390d78c13SJulien Lefrique 	}
83490d78c13SJulien Lefrique 
835772dccf4SJulien Lefrique 	param.im_protocols = im_protocols;
836772dccf4SJulien Lefrique 	param.tm_protocols = tm_protocols;
837772dccf4SJulien Lefrique 	rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)&param,
8386a2968aaSIlan Elias 			 msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
8396a2968aaSIlan Elias 
8406a2968aaSIlan Elias 	if (!rc)
841fe7c5800SSamuel Ortiz 		ndev->poll_prots = im_protocols;
8426a2968aaSIlan Elias 
8436a2968aaSIlan Elias 	return rc;
8446a2968aaSIlan Elias }
8456a2968aaSIlan Elias 
8466a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev)
8476a2968aaSIlan Elias {
8486a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
8496a2968aaSIlan Elias 
850019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) != NCI_DISCOVERY) &&
851019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) {
852ed1e0ad8SJoe Perches 		pr_err("unable to stop poll, since poll is not active\n");
8536a2968aaSIlan Elias 		return;
8546a2968aaSIlan Elias 	}
8556a2968aaSIlan Elias 
8569295b5b5SChristophe Ricard 	nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE,
8576a2968aaSIlan Elias 		    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
8586a2968aaSIlan Elias }
8596a2968aaSIlan Elias 
86090099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev,
86190099433SEric Lapuyade 			       struct nfc_target *target, __u32 protocol)
8626a2968aaSIlan Elias {
8636a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
864019c4fbaSIlan Elias 	struct nci_rf_discover_select_param param;
86590099433SEric Lapuyade 	struct nfc_target *nci_target = NULL;
866019c4fbaSIlan Elias 	int i;
867019c4fbaSIlan Elias 	int rc = 0;
8686a2968aaSIlan Elias 
86990099433SEric Lapuyade 	pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol);
8706a2968aaSIlan Elias 
871019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) &&
872019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
873ed1e0ad8SJoe Perches 		pr_err("there is no available target to activate\n");
8746a2968aaSIlan Elias 		return -EINVAL;
8756a2968aaSIlan Elias 	}
8766a2968aaSIlan Elias 
8776a2968aaSIlan Elias 	if (ndev->target_active_prot) {
878ed1e0ad8SJoe Perches 		pr_err("there is already an active target\n");
8796a2968aaSIlan Elias 		return -EBUSY;
8806a2968aaSIlan Elias 	}
8816a2968aaSIlan Elias 
882019c4fbaSIlan Elias 	for (i = 0; i < ndev->n_targets; i++) {
88390099433SEric Lapuyade 		if (ndev->targets[i].idx == target->idx) {
88490099433SEric Lapuyade 			nci_target = &ndev->targets[i];
885019c4fbaSIlan Elias 			break;
886019c4fbaSIlan Elias 		}
887019c4fbaSIlan Elias 	}
888019c4fbaSIlan Elias 
88990099433SEric Lapuyade 	if (!nci_target) {
890019c4fbaSIlan Elias 		pr_err("unable to find the selected target\n");
891019c4fbaSIlan Elias 		return -EINVAL;
892019c4fbaSIlan Elias 	}
893019c4fbaSIlan Elias 
89490099433SEric Lapuyade 	if (!(nci_target->supported_protocols & (1 << protocol))) {
895ed1e0ad8SJoe Perches 		pr_err("target does not support the requested protocol 0x%x\n",
8966a2968aaSIlan Elias 		       protocol);
8976a2968aaSIlan Elias 		return -EINVAL;
8986a2968aaSIlan Elias 	}
8996a2968aaSIlan Elias 
900019c4fbaSIlan Elias 	if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
90190099433SEric Lapuyade 		param.rf_discovery_id = nci_target->logical_idx;
9026a2968aaSIlan Elias 
903019c4fbaSIlan Elias 		if (protocol == NFC_PROTO_JEWEL)
904019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T1T;
905019c4fbaSIlan Elias 		else if (protocol == NFC_PROTO_MIFARE)
906019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T2T;
907019c4fbaSIlan Elias 		else if (protocol == NFC_PROTO_FELICA)
908019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T3T;
90901d719a2SSamuel Ortiz 		else if (protocol == NFC_PROTO_ISO14443 ||
91001d719a2SSamuel Ortiz 			 protocol == NFC_PROTO_ISO14443_B)
911019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
912019c4fbaSIlan Elias 		else
913019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
914019c4fbaSIlan Elias 
915019c4fbaSIlan Elias 		rc = nci_request(ndev, nci_rf_discover_select_req,
916019c4fbaSIlan Elias 				 (unsigned long)&param,
917019c4fbaSIlan Elias 				 msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT));
918019c4fbaSIlan Elias 	}
919019c4fbaSIlan Elias 
920019c4fbaSIlan Elias 	if (!rc)
921019c4fbaSIlan Elias 		ndev->target_active_prot = protocol;
922019c4fbaSIlan Elias 
923019c4fbaSIlan Elias 	return rc;
9246a2968aaSIlan Elias }
9256a2968aaSIlan Elias 
92690099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev,
92796d4581fSChristophe Ricard 				  struct nfc_target *target,
92896d4581fSChristophe Ricard 				  __u8 mode)
9296a2968aaSIlan Elias {
9306a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
93196d4581fSChristophe Ricard 	u8 nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE;
9326a2968aaSIlan Elias 
933767f19aeSIlan Elias 	pr_debug("entry\n");
9346a2968aaSIlan Elias 
9356a2968aaSIlan Elias 	if (!ndev->target_active_prot) {
936ed1e0ad8SJoe Perches 		pr_err("unable to deactivate target, no active target\n");
9376a2968aaSIlan Elias 		return;
9386a2968aaSIlan Elias 	}
9396a2968aaSIlan Elias 
9406a2968aaSIlan Elias 	ndev->target_active_prot = 0;
9416a2968aaSIlan Elias 
94296d4581fSChristophe Ricard 	switch (mode) {
94396d4581fSChristophe Ricard 	case NFC_TARGET_MODE_SLEEP:
94496d4581fSChristophe Ricard 		nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE;
94596d4581fSChristophe Ricard 		break;
94696d4581fSChristophe Ricard 	}
94796d4581fSChristophe Ricard 
9488939e47fSIlan Elias 	if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
94996d4581fSChristophe Ricard 		nci_request(ndev, nci_rf_deactivate_req, nci_mode,
9506a2968aaSIlan Elias 			    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
9516a2968aaSIlan Elias 	}
9526a2968aaSIlan Elias }
9536a2968aaSIlan Elias 
954767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
955767f19aeSIlan Elias 			   __u8 comm_mode, __u8 *gb, size_t gb_len)
956767f19aeSIlan Elias {
957767f19aeSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
958767f19aeSIlan Elias 	int rc;
959767f19aeSIlan Elias 
960767f19aeSIlan Elias 	pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
961767f19aeSIlan Elias 
962767f19aeSIlan Elias 	rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
963767f19aeSIlan Elias 	if (rc)
964767f19aeSIlan Elias 		return rc;
965767f19aeSIlan Elias 
966767f19aeSIlan Elias 	rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
967767f19aeSIlan Elias 					  ndev->remote_gb_len);
968767f19aeSIlan Elias 	if (!rc)
969767f19aeSIlan Elias 		rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
970767f19aeSIlan Elias 					NFC_RF_INITIATOR);
971767f19aeSIlan Elias 
972767f19aeSIlan Elias 	return rc;
973767f19aeSIlan Elias }
974767f19aeSIlan Elias 
975767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev)
976767f19aeSIlan Elias {
977d7979e13SJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
978d7979e13SJulien Lefrique 	int rc;
979d7979e13SJulien Lefrique 
980767f19aeSIlan Elias 	pr_debug("entry\n");
981767f19aeSIlan Elias 
982d7979e13SJulien Lefrique 	if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
98396d4581fSChristophe Ricard 		nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE);
984d7979e13SJulien Lefrique 	} else {
985d7979e13SJulien Lefrique 		if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
986d7979e13SJulien Lefrique 		    atomic_read(&ndev->state) == NCI_DISCOVERY) {
987d7979e13SJulien Lefrique 			nci_request(ndev, nci_rf_deactivate_req, 0,
988d7979e13SJulien Lefrique 				msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
989d7979e13SJulien Lefrique 		}
990d7979e13SJulien Lefrique 
991d7979e13SJulien Lefrique 		rc = nfc_tm_deactivated(nfc_dev);
992d7979e13SJulien Lefrique 		if (rc)
993d7979e13SJulien Lefrique 			pr_err("error when signaling tm deactivation\n");
994d7979e13SJulien Lefrique 	}
995767f19aeSIlan Elias 
996767f19aeSIlan Elias 	return 0;
997767f19aeSIlan Elias }
998767f19aeSIlan Elias 
999767f19aeSIlan Elias 
1000be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
10016a2968aaSIlan Elias 			  struct sk_buff *skb,
1002eb9bc6e9SSamuel Ortiz 			  data_exchange_cb_t cb, void *cb_context)
10036a2968aaSIlan Elias {
10046a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
100538f04c6bSIlan Elias 	int rc;
10064aeee687SChristophe Ricard 	struct nci_conn_info    *conn_info;
10074aeee687SChristophe Ricard 
100812bdf27dSChristophe Ricard 	conn_info = ndev->rf_conn_info;
10094aeee687SChristophe Ricard 	if (!conn_info)
10104aeee687SChristophe Ricard 		return -EPROTO;
10116a2968aaSIlan Elias 
101290099433SEric Lapuyade 	pr_debug("target_idx %d, len %d\n", target->idx, skb->len);
10136a2968aaSIlan Elias 
10146a2968aaSIlan Elias 	if (!ndev->target_active_prot) {
1015ed1e0ad8SJoe Perches 		pr_err("unable to exchange data, no active target\n");
10166a2968aaSIlan Elias 		return -EINVAL;
10176a2968aaSIlan Elias 	}
10186a2968aaSIlan Elias 
101938f04c6bSIlan Elias 	if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags))
102038f04c6bSIlan Elias 		return -EBUSY;
102138f04c6bSIlan Elias 
10226a2968aaSIlan Elias 	/* store cb and context to be used on receiving data */
10234aeee687SChristophe Ricard 	conn_info->data_exchange_cb = cb;
10244aeee687SChristophe Ricard 	conn_info->data_exchange_cb_context = cb_context;
10256a2968aaSIlan Elias 
1026e8c0dacdSIlan Elias 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
102738f04c6bSIlan Elias 	if (rc)
102838f04c6bSIlan Elias 		clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
102938f04c6bSIlan Elias 
103038f04c6bSIlan Elias 	return rc;
10316a2968aaSIlan Elias }
10326a2968aaSIlan Elias 
1033485f442fSJulien Lefrique static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
1034485f442fSJulien Lefrique {
1035485f442fSJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1036485f442fSJulien Lefrique 	int rc;
1037485f442fSJulien Lefrique 
1038485f442fSJulien Lefrique 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
1039485f442fSJulien Lefrique 	if (rc)
1040485f442fSJulien Lefrique 		pr_err("unable to send data\n");
1041485f442fSJulien Lefrique 
1042485f442fSJulien Lefrique 	return rc;
1043485f442fSJulien Lefrique }
1044485f442fSJulien Lefrique 
10450a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
10460a946301SSamuel Ortiz {
104793bca2bfSChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
104893bca2bfSChristophe Ricard 
104993bca2bfSChristophe Ricard 	if (ndev->ops->enable_se)
105093bca2bfSChristophe Ricard 		return ndev->ops->enable_se(ndev, se_idx);
105193bca2bfSChristophe Ricard 
10520a946301SSamuel Ortiz 	return 0;
10530a946301SSamuel Ortiz }
10540a946301SSamuel Ortiz 
10550a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
10560a946301SSamuel Ortiz {
1057e9ef9431SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1058e9ef9431SChristophe Ricard 
1059e9ef9431SChristophe Ricard 	if (ndev->ops->disable_se)
1060e9ef9431SChristophe Ricard 		return ndev->ops->disable_se(ndev, se_idx);
1061e9ef9431SChristophe Ricard 
10620a946301SSamuel Ortiz 	return 0;
10630a946301SSamuel Ortiz }
10640a946301SSamuel Ortiz 
10650a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev)
10660a946301SSamuel Ortiz {
1067fa00e8feSChristophe Ricard 	int r;
1068ba4db551SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1069ba4db551SChristophe Ricard 
1070fa00e8feSChristophe Ricard 	if (ndev->ops->discover_se) {
1071fa00e8feSChristophe Ricard 		r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE);
1072fa00e8feSChristophe Ricard 		if (r != NCI_STATUS_OK)
1073fa00e8feSChristophe Ricard 			return -EPROTO;
1074fa00e8feSChristophe Ricard 
1075ba4db551SChristophe Ricard 		return ndev->ops->discover_se(ndev);
1076fa00e8feSChristophe Ricard 	}
1077ba4db551SChristophe Ricard 
10780a946301SSamuel Ortiz 	return 0;
10790a946301SSamuel Ortiz }
10800a946301SSamuel Ortiz 
1081a688bf55SChristophe Ricard static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
1082a688bf55SChristophe Ricard 		     u8 *apdu, size_t apdu_length,
1083a688bf55SChristophe Ricard 		     se_io_cb_t cb, void *cb_context)
1084a688bf55SChristophe Ricard {
1085a688bf55SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1086a688bf55SChristophe Ricard 
1087a688bf55SChristophe Ricard 	if (ndev->ops->se_io)
1088a688bf55SChristophe Ricard 		return ndev->ops->se_io(ndev, se_idx, apdu,
1089a688bf55SChristophe Ricard 				apdu_length, cb, cb_context);
1090a688bf55SChristophe Ricard 
1091a688bf55SChristophe Ricard 	return 0;
1092a688bf55SChristophe Ricard }
1093a688bf55SChristophe Ricard 
109425af01edSClément Perrochaud static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
109525af01edSClément Perrochaud {
109625af01edSClément Perrochaud 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
109725af01edSClément Perrochaud 
109825af01edSClément Perrochaud 	if (!ndev->ops->fw_download)
109925af01edSClément Perrochaud 		return -ENOTSUPP;
110025af01edSClément Perrochaud 
110125af01edSClément Perrochaud 	return ndev->ops->fw_download(ndev, firmware_name);
110225af01edSClément Perrochaud }
110325af01edSClément Perrochaud 
11046a2968aaSIlan Elias static struct nfc_ops nci_nfc_ops = {
11056a2968aaSIlan Elias 	.dev_up = nci_dev_up,
11066a2968aaSIlan Elias 	.dev_down = nci_dev_down,
11076a2968aaSIlan Elias 	.start_poll = nci_start_poll,
11086a2968aaSIlan Elias 	.stop_poll = nci_stop_poll,
1109767f19aeSIlan Elias 	.dep_link_up = nci_dep_link_up,
1110767f19aeSIlan Elias 	.dep_link_down = nci_dep_link_down,
11116a2968aaSIlan Elias 	.activate_target = nci_activate_target,
11126a2968aaSIlan Elias 	.deactivate_target = nci_deactivate_target,
1113be9ae4ceSSamuel Ortiz 	.im_transceive = nci_transceive,
1114485f442fSJulien Lefrique 	.tm_send = nci_tm_send,
11150a946301SSamuel Ortiz 	.enable_se = nci_enable_se,
11160a946301SSamuel Ortiz 	.disable_se = nci_disable_se,
11170a946301SSamuel Ortiz 	.discover_se = nci_discover_se,
1118a688bf55SChristophe Ricard 	.se_io = nci_se_io,
111925af01edSClément Perrochaud 	.fw_download = nci_fw_download,
11206a2968aaSIlan Elias };
11216a2968aaSIlan Elias 
11226a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */
11236a2968aaSIlan Elias /**
11246a2968aaSIlan Elias  * nci_allocate_device - allocate a new nci device
11256a2968aaSIlan Elias  *
11266a2968aaSIlan Elias  * @ops: device operations
11276a2968aaSIlan Elias  * @supported_protocols: NFC protocols supported by the device
11286a2968aaSIlan Elias  */
11296a2968aaSIlan Elias struct nci_dev *nci_allocate_device(struct nci_ops *ops,
11306a2968aaSIlan Elias 				    __u32 supported_protocols,
1131eb9bc6e9SSamuel Ortiz 				    int tx_headroom, int tx_tailroom)
11326a2968aaSIlan Elias {
11338ebafde0SDan Carpenter 	struct nci_dev *ndev;
11346a2968aaSIlan Elias 
113524bf3304SJoe Perches 	pr_debug("supported_protocols 0x%x\n", supported_protocols);
11366a2968aaSIlan Elias 
11376a2968aaSIlan Elias 	if (!ops->open || !ops->close || !ops->send)
11388ebafde0SDan Carpenter 		return NULL;
11396a2968aaSIlan Elias 
11406a2968aaSIlan Elias 	if (!supported_protocols)
11418ebafde0SDan Carpenter 		return NULL;
11426a2968aaSIlan Elias 
11436a2968aaSIlan Elias 	ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL);
11446a2968aaSIlan Elias 	if (!ndev)
11458ebafde0SDan Carpenter 		return NULL;
11466a2968aaSIlan Elias 
11476a2968aaSIlan Elias 	ndev->ops = ops;
1148b6355e97SSamuel Ortiz 
1149b6355e97SSamuel Ortiz 	if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) {
1150b6355e97SSamuel Ortiz 		pr_err("Too many proprietary commands: %zd\n",
1151b6355e97SSamuel Ortiz 		       ops->n_prop_ops);
1152b6355e97SSamuel Ortiz 		ops->prop_ops = NULL;
1153b6355e97SSamuel Ortiz 		ops->n_prop_ops = 0;
1154b6355e97SSamuel Ortiz 	}
1155b6355e97SSamuel Ortiz 
11566a2968aaSIlan Elias 	ndev->tx_headroom = tx_headroom;
11576a2968aaSIlan Elias 	ndev->tx_tailroom = tx_tailroom;
11589bec44bfSAxel Lin 	init_completion(&ndev->req_completion);
11596a2968aaSIlan Elias 
11606a2968aaSIlan Elias 	ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
11616a2968aaSIlan Elias 					    supported_protocols,
11626a2968aaSIlan Elias 					    tx_headroom + NCI_DATA_HDR_SIZE,
11636a2968aaSIlan Elias 					    tx_tailroom);
11646a2968aaSIlan Elias 	if (!ndev->nfc_dev)
116511f54f22SChristophe Ricard 		goto free_nci;
116611f54f22SChristophe Ricard 
116711f54f22SChristophe Ricard 	ndev->hci_dev = nci_hci_allocate(ndev);
116811f54f22SChristophe Ricard 	if (!ndev->hci_dev)
116911f54f22SChristophe Ricard 		goto free_nfc;
11706a2968aaSIlan Elias 
11716a2968aaSIlan Elias 	nfc_set_drvdata(ndev->nfc_dev, ndev);
11726a2968aaSIlan Elias 
11738ebafde0SDan Carpenter 	return ndev;
11746a2968aaSIlan Elias 
117511f54f22SChristophe Ricard free_nfc:
117611f54f22SChristophe Ricard 	kfree(ndev->nfc_dev);
117711f54f22SChristophe Ricard 
117811f54f22SChristophe Ricard free_nci:
11796a2968aaSIlan Elias 	kfree(ndev);
11808ebafde0SDan Carpenter 	return NULL;
11816a2968aaSIlan Elias }
11826a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device);
11836a2968aaSIlan Elias 
11846a2968aaSIlan Elias /**
11856a2968aaSIlan Elias  * nci_free_device - deallocate nci device
11866a2968aaSIlan Elias  *
11876a2968aaSIlan Elias  * @ndev: The nci device to deallocate
11886a2968aaSIlan Elias  */
11896a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev)
11906a2968aaSIlan Elias {
11916a2968aaSIlan Elias 	nfc_free_device(ndev->nfc_dev);
11926a2968aaSIlan Elias 	kfree(ndev);
11936a2968aaSIlan Elias }
11946a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device);
11956a2968aaSIlan Elias 
11966a2968aaSIlan Elias /**
11976a2968aaSIlan Elias  * nci_register_device - register a nci device in the nfc subsystem
11986a2968aaSIlan Elias  *
11996a2968aaSIlan Elias  * @dev: The nci device to register
12006a2968aaSIlan Elias  */
12016a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev)
12026a2968aaSIlan Elias {
12036a2968aaSIlan Elias 	int rc;
12046a2968aaSIlan Elias 	struct device *dev = &ndev->nfc_dev->dev;
12056a2968aaSIlan Elias 	char name[32];
12066a2968aaSIlan Elias 
12076a2968aaSIlan Elias 	ndev->flags = 0;
12086a2968aaSIlan Elias 
12096a2968aaSIlan Elias 	INIT_WORK(&ndev->cmd_work, nci_cmd_work);
12106a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev));
12116a2968aaSIlan Elias 	ndev->cmd_wq = create_singlethread_workqueue(name);
12126a2968aaSIlan Elias 	if (!ndev->cmd_wq) {
12136a2968aaSIlan Elias 		rc = -ENOMEM;
12143c1c0f5dSVincent Cuissard 		goto exit;
12156a2968aaSIlan Elias 	}
12166a2968aaSIlan Elias 
12176a2968aaSIlan Elias 	INIT_WORK(&ndev->rx_work, nci_rx_work);
12186a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev));
12196a2968aaSIlan Elias 	ndev->rx_wq = create_singlethread_workqueue(name);
12206a2968aaSIlan Elias 	if (!ndev->rx_wq) {
12216a2968aaSIlan Elias 		rc = -ENOMEM;
12226a2968aaSIlan Elias 		goto destroy_cmd_wq_exit;
12236a2968aaSIlan Elias 	}
12246a2968aaSIlan Elias 
12256a2968aaSIlan Elias 	INIT_WORK(&ndev->tx_work, nci_tx_work);
12266a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev));
12276a2968aaSIlan Elias 	ndev->tx_wq = create_singlethread_workqueue(name);
12286a2968aaSIlan Elias 	if (!ndev->tx_wq) {
12296a2968aaSIlan Elias 		rc = -ENOMEM;
12306a2968aaSIlan Elias 		goto destroy_rx_wq_exit;
12316a2968aaSIlan Elias 	}
12326a2968aaSIlan Elias 
12336a2968aaSIlan Elias 	skb_queue_head_init(&ndev->cmd_q);
12346a2968aaSIlan Elias 	skb_queue_head_init(&ndev->rx_q);
12356a2968aaSIlan Elias 	skb_queue_head_init(&ndev->tx_q);
12366a2968aaSIlan Elias 
12376a2968aaSIlan Elias 	setup_timer(&ndev->cmd_timer, nci_cmd_timer,
12386a2968aaSIlan Elias 		    (unsigned long) ndev);
1239c4bf98b2SIlan Elias 	setup_timer(&ndev->data_timer, nci_data_timer,
1240c4bf98b2SIlan Elias 		    (unsigned long) ndev);
12416a2968aaSIlan Elias 
12426a2968aaSIlan Elias 	mutex_init(&ndev->req_lock);
12434aeee687SChristophe Ricard 	INIT_LIST_HEAD(&ndev->conn_info_list);
12446a2968aaSIlan Elias 
12453c1c0f5dSVincent Cuissard 	rc = nfc_register_device(ndev->nfc_dev);
12463c1c0f5dSVincent Cuissard 	if (rc)
12473c1c0f5dSVincent Cuissard 		goto destroy_rx_wq_exit;
12483c1c0f5dSVincent Cuissard 
12496a2968aaSIlan Elias 	goto exit;
12506a2968aaSIlan Elias 
12516a2968aaSIlan Elias destroy_rx_wq_exit:
12526a2968aaSIlan Elias 	destroy_workqueue(ndev->rx_wq);
12536a2968aaSIlan Elias 
12546a2968aaSIlan Elias destroy_cmd_wq_exit:
12556a2968aaSIlan Elias 	destroy_workqueue(ndev->cmd_wq);
12566a2968aaSIlan Elias 
12576a2968aaSIlan Elias exit:
12586a2968aaSIlan Elias 	return rc;
12596a2968aaSIlan Elias }
12606a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device);
12616a2968aaSIlan Elias 
12626a2968aaSIlan Elias /**
12636a2968aaSIlan Elias  * nci_unregister_device - unregister a nci device in the nfc subsystem
12646a2968aaSIlan Elias  *
12656a2968aaSIlan Elias  * @dev: The nci device to unregister
12666a2968aaSIlan Elias  */
12676a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev)
12686a2968aaSIlan Elias {
12694aeee687SChristophe Ricard 	struct nci_conn_info    *conn_info, *n;
12704aeee687SChristophe Ricard 
12716a2968aaSIlan Elias 	nci_close_device(ndev);
12726a2968aaSIlan Elias 
12736a2968aaSIlan Elias 	destroy_workqueue(ndev->cmd_wq);
12746a2968aaSIlan Elias 	destroy_workqueue(ndev->rx_wq);
12756a2968aaSIlan Elias 	destroy_workqueue(ndev->tx_wq);
12766a2968aaSIlan Elias 
12774aeee687SChristophe Ricard 	list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) {
12784aeee687SChristophe Ricard 		list_del(&conn_info->list);
12794aeee687SChristophe Ricard 		/* conn_info is allocated with devm_kzalloc */
12804aeee687SChristophe Ricard 	}
12814aeee687SChristophe Ricard 
12826a2968aaSIlan Elias 	nfc_unregister_device(ndev->nfc_dev);
12836a2968aaSIlan Elias }
12846a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device);
12856a2968aaSIlan Elias 
12866a2968aaSIlan Elias /**
12876a2968aaSIlan Elias  * nci_recv_frame - receive frame from NCI drivers
12886a2968aaSIlan Elias  *
12891095e69fSFrederic Danis  * @ndev: The nci device
12906a2968aaSIlan Elias  * @skb: The sk_buff to receive
12916a2968aaSIlan Elias  */
12921095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
12936a2968aaSIlan Elias {
129424bf3304SJoe Perches 	pr_debug("len %d\n", skb->len);
12956a2968aaSIlan Elias 
1296874934f4SSzymon Janc 	if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
1297874934f4SSzymon Janc 	    !test_bit(NCI_INIT, &ndev->flags))) {
12986a2968aaSIlan Elias 		kfree_skb(skb);
12996a2968aaSIlan Elias 		return -ENXIO;
13006a2968aaSIlan Elias 	}
13016a2968aaSIlan Elias 
13026a2968aaSIlan Elias 	/* Queue frame for rx worker thread */
13036a2968aaSIlan Elias 	skb_queue_tail(&ndev->rx_q, skb);
13046a2968aaSIlan Elias 	queue_work(ndev->rx_wq, &ndev->rx_work);
13056a2968aaSIlan Elias 
13066a2968aaSIlan Elias 	return 0;
13076a2968aaSIlan Elias }
13086a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame);
13096a2968aaSIlan Elias 
1310e5629d29SVincent Cuissard int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
13116a2968aaSIlan Elias {
131224bf3304SJoe Perches 	pr_debug("len %d\n", skb->len);
13136a2968aaSIlan Elias 
13146a2968aaSIlan Elias 	if (!ndev) {
13156a2968aaSIlan Elias 		kfree_skb(skb);
13166a2968aaSIlan Elias 		return -ENODEV;
13176a2968aaSIlan Elias 	}
13186a2968aaSIlan Elias 
13196a2968aaSIlan Elias 	/* Get rid of skb owner, prior to sending to the driver. */
13206a2968aaSIlan Elias 	skb_orphan(skb);
13216a2968aaSIlan Elias 
132205158296SHiren Tandel 	/* Send copy to sniffer */
132305158296SHiren Tandel 	nfc_send_to_raw_sock(ndev->nfc_dev, skb,
132405158296SHiren Tandel 			     RAW_PAYLOAD_NCI, NFC_DIRECTION_TX);
132505158296SHiren Tandel 
13261095e69fSFrederic Danis 	return ndev->ops->send(ndev, skb);
13276a2968aaSIlan Elias }
1328e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_frame);
13296a2968aaSIlan Elias 
13306a2968aaSIlan Elias /* Send NCI command */
13316a2968aaSIlan Elias int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload)
13326a2968aaSIlan Elias {
13336a2968aaSIlan Elias 	struct nci_ctrl_hdr *hdr;
13346a2968aaSIlan Elias 	struct sk_buff *skb;
13356a2968aaSIlan Elias 
133624bf3304SJoe Perches 	pr_debug("opcode 0x%x, plen %d\n", opcode, plen);
13376a2968aaSIlan Elias 
13386a2968aaSIlan Elias 	skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL);
13396a2968aaSIlan Elias 	if (!skb) {
1340ed1e0ad8SJoe Perches 		pr_err("no memory for command\n");
13416a2968aaSIlan Elias 		return -ENOMEM;
13426a2968aaSIlan Elias 	}
13436a2968aaSIlan Elias 
13444df864c1SJohannes Berg 	hdr = skb_put(skb, NCI_CTRL_HDR_SIZE);
13456a2968aaSIlan Elias 	hdr->gid = nci_opcode_gid(opcode);
13466a2968aaSIlan Elias 	hdr->oid = nci_opcode_oid(opcode);
13476a2968aaSIlan Elias 	hdr->plen = plen;
13486a2968aaSIlan Elias 
13496a2968aaSIlan Elias 	nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT);
13506a2968aaSIlan Elias 	nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
13516a2968aaSIlan Elias 
13526a2968aaSIlan Elias 	if (plen)
135359ae1d12SJohannes Berg 		skb_put_data(skb, payload, plen);
13546a2968aaSIlan Elias 
13556a2968aaSIlan Elias 	skb_queue_tail(&ndev->cmd_q, skb);
13566a2968aaSIlan Elias 	queue_work(ndev->cmd_wq, &ndev->cmd_work);
13576a2968aaSIlan Elias 
13586a2968aaSIlan Elias 	return 0;
13596a2968aaSIlan Elias }
1360e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_cmd);
13616a2968aaSIlan Elias 
1362b6355e97SSamuel Ortiz /* Proprietary commands API */
136322e4bd09SRobert Dolca static struct nci_driver_ops *ops_cmd_lookup(struct nci_driver_ops *ops,
13640a97a3cbSRobert Dolca 					     size_t n_ops,
1365b6355e97SSamuel Ortiz 					     __u16 opcode)
1366b6355e97SSamuel Ortiz {
1367b6355e97SSamuel Ortiz 	size_t i;
136822e4bd09SRobert Dolca 	struct nci_driver_ops *op;
1369b6355e97SSamuel Ortiz 
13700a97a3cbSRobert Dolca 	if (!ops || !n_ops)
1371b6355e97SSamuel Ortiz 		return NULL;
1372b6355e97SSamuel Ortiz 
13730a97a3cbSRobert Dolca 	for (i = 0; i < n_ops; i++) {
13740a97a3cbSRobert Dolca 		op = &ops[i];
13750a97a3cbSRobert Dolca 		if (op->opcode == opcode)
13760a97a3cbSRobert Dolca 			return op;
1377b6355e97SSamuel Ortiz 	}
1378b6355e97SSamuel Ortiz 
1379b6355e97SSamuel Ortiz 	return NULL;
1380b6355e97SSamuel Ortiz }
1381b6355e97SSamuel Ortiz 
13820a97a3cbSRobert Dolca static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
138322e4bd09SRobert Dolca 			     struct sk_buff *skb, struct nci_driver_ops *ops,
13840a97a3cbSRobert Dolca 			     size_t n_ops)
1385b6355e97SSamuel Ortiz {
138622e4bd09SRobert Dolca 	struct nci_driver_ops *op;
1387b6355e97SSamuel Ortiz 
13880a97a3cbSRobert Dolca 	op = ops_cmd_lookup(ops, n_ops, rsp_opcode);
13890a97a3cbSRobert Dolca 	if (!op || !op->rsp)
1390b6355e97SSamuel Ortiz 		return -ENOTSUPP;
1391b6355e97SSamuel Ortiz 
13920a97a3cbSRobert Dolca 	return op->rsp(ndev, skb);
1393b6355e97SSamuel Ortiz }
1394b6355e97SSamuel Ortiz 
13950a97a3cbSRobert Dolca static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
139622e4bd09SRobert Dolca 			     struct sk_buff *skb, struct nci_driver_ops *ops,
13970a97a3cbSRobert Dolca 			     size_t n_ops)
1398b6355e97SSamuel Ortiz {
139922e4bd09SRobert Dolca 	struct nci_driver_ops *op;
1400b6355e97SSamuel Ortiz 
14010a97a3cbSRobert Dolca 	op = ops_cmd_lookup(ops, n_ops, ntf_opcode);
14020a97a3cbSRobert Dolca 	if (!op || !op->ntf)
1403b6355e97SSamuel Ortiz 		return -ENOTSUPP;
1404b6355e97SSamuel Ortiz 
14050a97a3cbSRobert Dolca 	return op->ntf(ndev, skb);
14060a97a3cbSRobert Dolca }
14070a97a3cbSRobert Dolca 
1408f1163174SRobert Dolca int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
14090a97a3cbSRobert Dolca 			struct sk_buff *skb)
14100a97a3cbSRobert Dolca {
14110a97a3cbSRobert Dolca 	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops,
14120a97a3cbSRobert Dolca 				 ndev->ops->n_prop_ops);
14130a97a3cbSRobert Dolca }
14140a97a3cbSRobert Dolca 
1415f1163174SRobert Dolca int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
14160a97a3cbSRobert Dolca 			struct sk_buff *skb)
14170a97a3cbSRobert Dolca {
14180a97a3cbSRobert Dolca 	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops,
14190a97a3cbSRobert Dolca 				 ndev->ops->n_prop_ops);
14200a97a3cbSRobert Dolca }
14210a97a3cbSRobert Dolca 
1422f1163174SRobert Dolca int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode,
14230a97a3cbSRobert Dolca 			struct sk_buff *skb)
14240a97a3cbSRobert Dolca {
14250a97a3cbSRobert Dolca 	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops,
14260a97a3cbSRobert Dolca 				  ndev->ops->n_core_ops);
14270a97a3cbSRobert Dolca }
14280a97a3cbSRobert Dolca 
1429f1163174SRobert Dolca int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode,
14300a97a3cbSRobert Dolca 			struct sk_buff *skb)
14310a97a3cbSRobert Dolca {
14320a97a3cbSRobert Dolca 	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops,
14330a97a3cbSRobert Dolca 				 ndev->ops->n_core_ops);
1434b6355e97SSamuel Ortiz }
1435b6355e97SSamuel Ortiz 
14366a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */
14376a2968aaSIlan Elias 
14386a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work)
14396a2968aaSIlan Elias {
14406a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work);
14414aeee687SChristophe Ricard 	struct nci_conn_info    *conn_info;
14426a2968aaSIlan Elias 	struct sk_buff *skb;
14436a2968aaSIlan Elias 
14444aeee687SChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
14454aeee687SChristophe Ricard 	if (!conn_info)
14464aeee687SChristophe Ricard 		return;
14474aeee687SChristophe Ricard 
14484aeee687SChristophe Ricard 	pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt));
14496a2968aaSIlan Elias 
14506a2968aaSIlan Elias 	/* Send queued tx data */
14514aeee687SChristophe Ricard 	while (atomic_read(&conn_info->credits_cnt)) {
14526a2968aaSIlan Elias 		skb = skb_dequeue(&ndev->tx_q);
14536a2968aaSIlan Elias 		if (!skb)
14546a2968aaSIlan Elias 			return;
14556a2968aaSIlan Elias 
1456db98c829SIlan Elias 		/* Check if data flow control is used */
14574aeee687SChristophe Ricard 		if (atomic_read(&conn_info->credits_cnt) !=
1458db98c829SIlan Elias 		    NCI_DATA_FLOW_CONTROL_NOT_USED)
14594aeee687SChristophe Ricard 			atomic_dec(&conn_info->credits_cnt);
14606a2968aaSIlan Elias 
146120c239c1SJoe Perches 		pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
14626a2968aaSIlan Elias 			 nci_pbf(skb->data),
14636a2968aaSIlan Elias 			 nci_conn_id(skb->data),
14646a2968aaSIlan Elias 			 nci_plen(skb->data));
14656a2968aaSIlan Elias 
14661095e69fSFrederic Danis 		nci_send_frame(ndev, skb);
1467c4bf98b2SIlan Elias 
1468c4bf98b2SIlan Elias 		mod_timer(&ndev->data_timer,
1469c4bf98b2SIlan Elias 			  jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
14706a2968aaSIlan Elias 	}
14716a2968aaSIlan Elias }
14726a2968aaSIlan Elias 
14736a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */
14746a2968aaSIlan Elias 
14756a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work)
14766a2968aaSIlan Elias {
14776a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work);
14786a2968aaSIlan Elias 	struct sk_buff *skb;
14796a2968aaSIlan Elias 
14806a2968aaSIlan Elias 	while ((skb = skb_dequeue(&ndev->rx_q))) {
148105158296SHiren Tandel 
148205158296SHiren Tandel 		/* Send copy to sniffer */
148305158296SHiren Tandel 		nfc_send_to_raw_sock(ndev->nfc_dev, skb,
148405158296SHiren Tandel 				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
148505158296SHiren Tandel 
14866a2968aaSIlan Elias 		/* Process frame */
14876a2968aaSIlan Elias 		switch (nci_mt(skb->data)) {
14886a2968aaSIlan Elias 		case NCI_MT_RSP_PKT:
14896a2968aaSIlan Elias 			nci_rsp_packet(ndev, skb);
14906a2968aaSIlan Elias 			break;
14916a2968aaSIlan Elias 
14926a2968aaSIlan Elias 		case NCI_MT_NTF_PKT:
14936a2968aaSIlan Elias 			nci_ntf_packet(ndev, skb);
14946a2968aaSIlan Elias 			break;
14956a2968aaSIlan Elias 
14966a2968aaSIlan Elias 		case NCI_MT_DATA_PKT:
14976a2968aaSIlan Elias 			nci_rx_data_packet(ndev, skb);
14986a2968aaSIlan Elias 			break;
14996a2968aaSIlan Elias 
15006a2968aaSIlan Elias 		default:
1501ed1e0ad8SJoe Perches 			pr_err("unknown MT 0x%x\n", nci_mt(skb->data));
15026a2968aaSIlan Elias 			kfree_skb(skb);
15036a2968aaSIlan Elias 			break;
15046a2968aaSIlan Elias 		}
15056a2968aaSIlan Elias 	}
1506c4bf98b2SIlan Elias 
1507c4bf98b2SIlan Elias 	/* check if a data exchange timout has occurred */
1508c4bf98b2SIlan Elias 	if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
1509c4bf98b2SIlan Elias 		/* complete the data exchange transaction, if exists */
1510c4bf98b2SIlan Elias 		if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
15114aeee687SChristophe Ricard 			nci_data_exchange_complete(ndev, NULL,
15124aeee687SChristophe Ricard 						   ndev->cur_conn_id,
15134aeee687SChristophe Ricard 						   -ETIMEDOUT);
1514c4bf98b2SIlan Elias 
1515c4bf98b2SIlan Elias 		clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
1516c4bf98b2SIlan Elias 	}
15176a2968aaSIlan Elias }
15186a2968aaSIlan Elias 
15196a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */
15206a2968aaSIlan Elias 
15216a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work)
15226a2968aaSIlan Elias {
15236a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work);
15246a2968aaSIlan Elias 	struct sk_buff *skb;
15256a2968aaSIlan Elias 
152624bf3304SJoe Perches 	pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt));
15276a2968aaSIlan Elias 
15286a2968aaSIlan Elias 	/* Send queued command */
15296a2968aaSIlan Elias 	if (atomic_read(&ndev->cmd_cnt)) {
15306a2968aaSIlan Elias 		skb = skb_dequeue(&ndev->cmd_q);
15316a2968aaSIlan Elias 		if (!skb)
15326a2968aaSIlan Elias 			return;
15336a2968aaSIlan Elias 
15346a2968aaSIlan Elias 		atomic_dec(&ndev->cmd_cnt);
15356a2968aaSIlan Elias 
153620c239c1SJoe Perches 		pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n",
15376a2968aaSIlan Elias 			 nci_pbf(skb->data),
15386a2968aaSIlan Elias 			 nci_opcode_gid(nci_opcode(skb->data)),
15396a2968aaSIlan Elias 			 nci_opcode_oid(nci_opcode(skb->data)),
15406a2968aaSIlan Elias 			 nci_plen(skb->data));
15416a2968aaSIlan Elias 
15421095e69fSFrederic Danis 		nci_send_frame(ndev, skb);
15436a2968aaSIlan Elias 
15446a2968aaSIlan Elias 		mod_timer(&ndev->cmd_timer,
15456a2968aaSIlan Elias 			  jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
15466a2968aaSIlan Elias 	}
15476a2968aaSIlan Elias }
15488a70e7f8SDave Jones 
15498a70e7f8SDave Jones MODULE_LICENSE("GPL");
1550