xref: /openbmc/linux/net/nfc/nci/core.c (revision 46e72ebc)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
26a2968aaSIlan Elias /*
36a2968aaSIlan Elias  *  The NFC Controller Interface is the communication protocol between an
46a2968aaSIlan Elias  *  NFC Controller (NFCC) and a Device Host (DH).
56a2968aaSIlan Elias  *
66a2968aaSIlan Elias  *  Copyright (C) 2011 Texas Instruments, Inc.
7772dccf4SJulien Lefrique  *  Copyright (C) 2014 Marvell International Ltd.
86a2968aaSIlan Elias  *
96a2968aaSIlan Elias  *  Written by Ilan Elias <ilane@ti.com>
106a2968aaSIlan Elias  *
116a2968aaSIlan Elias  *  Acknowledgements:
126a2968aaSIlan Elias  *  This file is based on hci_core.c, which was written
136a2968aaSIlan Elias  *  by Maxim Krasnyansky.
146a2968aaSIlan Elias  */
156a2968aaSIlan Elias 
1652858b51SSamuel Ortiz #define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
17ed1e0ad8SJoe Perches 
188a70e7f8SDave Jones #include <linux/module.h>
19b6355e97SSamuel Ortiz #include <linux/kernel.h>
206a2968aaSIlan Elias #include <linux/types.h>
216a2968aaSIlan Elias #include <linux/workqueue.h>
226a2968aaSIlan Elias #include <linux/completion.h>
23bc3b2d7fSPaul Gortmaker #include <linux/export.h>
246a2968aaSIlan Elias #include <linux/sched.h>
256a2968aaSIlan Elias #include <linux/bitops.h>
266a2968aaSIlan Elias #include <linux/skbuff.h>
277e8cdc97SDmitry Vyukov #include <linux/kcov.h>
286a2968aaSIlan Elias 
296a2968aaSIlan Elias #include "../nfc.h"
306a2968aaSIlan Elias #include <net/nfc/nci.h>
316a2968aaSIlan Elias #include <net/nfc/nci_core.h>
326a2968aaSIlan Elias #include <linux/nfc.h>
336a2968aaSIlan Elias 
34b16ae716SChristophe Ricard struct core_conn_create_data {
35b16ae716SChristophe Ricard 	int length;
36b16ae716SChristophe Ricard 	struct nci_core_conn_create_cmd *cmd;
37b16ae716SChristophe Ricard };
38b16ae716SChristophe Ricard 
396a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work);
406a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work);
416a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work);
426a2968aaSIlan Elias 
nci_get_conn_info_by_conn_id(struct nci_dev * ndev,int conn_id)434aeee687SChristophe Ricard struct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
444aeee687SChristophe Ricard 						   int conn_id)
454aeee687SChristophe Ricard {
464aeee687SChristophe Ricard 	struct nci_conn_info *conn_info;
474aeee687SChristophe Ricard 
484aeee687SChristophe Ricard 	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
494aeee687SChristophe Ricard 		if (conn_info->conn_id == conn_id)
504aeee687SChristophe Ricard 			return conn_info;
514aeee687SChristophe Ricard 	}
524aeee687SChristophe Ricard 
534aeee687SChristophe Ricard 	return NULL;
544aeee687SChristophe Ricard }
554aeee687SChristophe Ricard 
nci_get_conn_info_by_dest_type_params(struct nci_dev * ndev,u8 dest_type,const struct dest_spec_params * params)569b8d1a4cSChristophe Ricard int nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type,
57ddecf555SKrzysztof Kozlowski 					  const struct dest_spec_params *params)
5885b9ce9aSRobert Dolca {
59ddecf555SKrzysztof Kozlowski 	const struct nci_conn_info *conn_info;
6085b9ce9aSRobert Dolca 
6185b9ce9aSRobert Dolca 	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
629b8d1a4cSChristophe Ricard 		if (conn_info->dest_type == dest_type) {
639b8d1a4cSChristophe Ricard 			if (!params)
6485b9ce9aSRobert Dolca 				return conn_info->conn_id;
6503036184SGustavo A. R. Silva 
669b8d1a4cSChristophe Ricard 			if (params->id == conn_info->dest_params->id &&
679b8d1a4cSChristophe Ricard 			    params->protocol == conn_info->dest_params->protocol)
689b8d1a4cSChristophe Ricard 				return conn_info->conn_id;
699b8d1a4cSChristophe Ricard 		}
709b8d1a4cSChristophe Ricard 	}
7185b9ce9aSRobert Dolca 
7285b9ce9aSRobert Dolca 	return -EINVAL;
7385b9ce9aSRobert Dolca }
749b8d1a4cSChristophe Ricard EXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params);
7585b9ce9aSRobert Dolca 
766a2968aaSIlan Elias /* ---- NCI requests ---- */
776a2968aaSIlan Elias 
nci_req_complete(struct nci_dev * ndev,int result)786a2968aaSIlan Elias void nci_req_complete(struct nci_dev *ndev, int result)
796a2968aaSIlan Elias {
806a2968aaSIlan Elias 	if (ndev->req_status == NCI_REQ_PEND) {
816a2968aaSIlan Elias 		ndev->req_result = result;
826a2968aaSIlan Elias 		ndev->req_status = NCI_REQ_DONE;
836a2968aaSIlan Elias 		complete(&ndev->req_completion);
846a2968aaSIlan Elias 	}
856a2968aaSIlan Elias }
862df7f8c6SSamuel Ortiz EXPORT_SYMBOL(nci_req_complete);
876a2968aaSIlan Elias 
nci_req_cancel(struct nci_dev * ndev,int err)886a2968aaSIlan Elias static void nci_req_cancel(struct nci_dev *ndev, int err)
896a2968aaSIlan Elias {
906a2968aaSIlan Elias 	if (ndev->req_status == NCI_REQ_PEND) {
916a2968aaSIlan Elias 		ndev->req_result = err;
926a2968aaSIlan Elias 		ndev->req_status = NCI_REQ_CANCELED;
936a2968aaSIlan Elias 		complete(&ndev->req_completion);
946a2968aaSIlan Elias 	}
956a2968aaSIlan Elias }
966a2968aaSIlan Elias 
976a2968aaSIlan Elias /* Execute request and wait for completion. */
__nci_request(struct nci_dev * ndev,void (* req)(struct nci_dev * ndev,const void * opt),const void * opt,__u32 timeout)986a2968aaSIlan Elias static int __nci_request(struct nci_dev *ndev,
9935d7a6f1SKrzysztof Kozlowski 			 void (*req)(struct nci_dev *ndev, const void *opt),
10035d7a6f1SKrzysztof Kozlowski 			 const void *opt, __u32 timeout)
1016a2968aaSIlan Elias {
1026a2968aaSIlan Elias 	int rc = 0;
103f8c141c3SDan Carpenter 	long completion_rc;
1046a2968aaSIlan Elias 
1056a2968aaSIlan Elias 	ndev->req_status = NCI_REQ_PEND;
1066a2968aaSIlan Elias 
1079bec44bfSAxel Lin 	reinit_completion(&ndev->req_completion);
1086a2968aaSIlan Elias 	req(ndev, opt);
109eb9bc6e9SSamuel Ortiz 	completion_rc =
110eb9bc6e9SSamuel Ortiz 		wait_for_completion_interruptible_timeout(&ndev->req_completion,
1116a2968aaSIlan Elias 							  timeout);
1126a2968aaSIlan Elias 
11320c239c1SJoe Perches 	pr_debug("wait_for_completion return %ld\n", completion_rc);
1146a2968aaSIlan Elias 
1156a2968aaSIlan Elias 	if (completion_rc > 0) {
1166a2968aaSIlan Elias 		switch (ndev->req_status) {
1176a2968aaSIlan Elias 		case NCI_REQ_DONE:
1186a2968aaSIlan Elias 			rc = nci_to_errno(ndev->req_result);
1196a2968aaSIlan Elias 			break;
1206a2968aaSIlan Elias 
1216a2968aaSIlan Elias 		case NCI_REQ_CANCELED:
1226a2968aaSIlan Elias 			rc = -ndev->req_result;
1236a2968aaSIlan Elias 			break;
1246a2968aaSIlan Elias 
1256a2968aaSIlan Elias 		default:
1266a2968aaSIlan Elias 			rc = -ETIMEDOUT;
1276a2968aaSIlan Elias 			break;
1286a2968aaSIlan Elias 		}
1296a2968aaSIlan Elias 	} else {
130ed1e0ad8SJoe Perches 		pr_err("wait_for_completion_interruptible_timeout failed %ld\n",
1316a2968aaSIlan Elias 		       completion_rc);
1326a2968aaSIlan Elias 
1336a2968aaSIlan Elias 		rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc));
1346a2968aaSIlan Elias 	}
1356a2968aaSIlan Elias 
1366a2968aaSIlan Elias 	ndev->req_status = ndev->req_result = 0;
1376a2968aaSIlan Elias 
1386a2968aaSIlan Elias 	return rc;
1396a2968aaSIlan Elias }
1406a2968aaSIlan Elias 
nci_request(struct nci_dev * ndev,void (* req)(struct nci_dev * ndev,const void * opt),const void * opt,__u32 timeout)14111f54f22SChristophe Ricard inline int nci_request(struct nci_dev *ndev,
142eb9bc6e9SSamuel Ortiz 		       void (*req)(struct nci_dev *ndev,
14335d7a6f1SKrzysztof Kozlowski 				   const void *opt),
14435d7a6f1SKrzysztof Kozlowski 		       const void *opt, __u32 timeout)
1456a2968aaSIlan Elias {
1466a2968aaSIlan Elias 	int rc;
1476a2968aaSIlan Elias 
1486a2968aaSIlan Elias 	/* Serialize all requests */
1496a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
15086cdf8e3SLin Ma 	/* check the state after obtaing the lock against any races
15186cdf8e3SLin Ma 	 * from nci_close_device when the device gets removed.
15286cdf8e3SLin Ma 	 */
15386cdf8e3SLin Ma 	if (test_bit(NCI_UP, &ndev->flags))
1546a2968aaSIlan Elias 		rc = __nci_request(ndev, req, opt, timeout);
15586cdf8e3SLin Ma 	else
15686cdf8e3SLin Ma 		rc = -ENETDOWN;
1576a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
1586a2968aaSIlan Elias 
1596a2968aaSIlan Elias 	return rc;
1606a2968aaSIlan Elias }
1616a2968aaSIlan Elias 
nci_reset_req(struct nci_dev * ndev,const void * opt)16235d7a6f1SKrzysztof Kozlowski static void nci_reset_req(struct nci_dev *ndev, const void *opt)
1636a2968aaSIlan Elias {
164e8c0dacdSIlan Elias 	struct nci_core_reset_cmd cmd;
165e8c0dacdSIlan Elias 
166e8c0dacdSIlan Elias 	cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG;
167e8c0dacdSIlan Elias 	nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd);
1686a2968aaSIlan Elias }
1696a2968aaSIlan Elias 
nci_init_req(struct nci_dev * ndev,const void * opt)17035d7a6f1SKrzysztof Kozlowski static void nci_init_req(struct nci_dev *ndev, const void *opt)
1716a2968aaSIlan Elias {
172bcd684aaSBongsu Jeon 	u8 plen = 0;
173bcd684aaSBongsu Jeon 
174bcd684aaSBongsu Jeon 	if (opt)
175bcd684aaSBongsu Jeon 		plen = sizeof(struct nci_core_init_v2_cmd);
176bcd684aaSBongsu Jeon 
17735d7a6f1SKrzysztof Kozlowski 	nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, plen, opt);
1786a2968aaSIlan Elias }
1796a2968aaSIlan Elias 
nci_init_complete_req(struct nci_dev * ndev,const void * opt)18035d7a6f1SKrzysztof Kozlowski static void nci_init_complete_req(struct nci_dev *ndev, const void *opt)
1816a2968aaSIlan Elias {
1822eb1dc10SIlan Elias 	struct nci_rf_disc_map_cmd cmd;
1832eb1dc10SIlan Elias 	struct disc_map_config *cfg = cmd.mapping_configs;
1842eb1dc10SIlan Elias 	__u8 *num = &cmd.num_mapping_configs;
1856a2968aaSIlan Elias 	int i;
1866a2968aaSIlan Elias 
1876a2968aaSIlan Elias 	/* set rf mapping configurations */
1882eb1dc10SIlan Elias 	*num = 0;
1896a2968aaSIlan Elias 
1906a2968aaSIlan Elias 	/* by default mapping is set to NCI_RF_INTERFACE_FRAME */
1916a2968aaSIlan Elias 	for (i = 0; i < ndev->num_supported_rf_interfaces; i++) {
1926a2968aaSIlan Elias 		if (ndev->supported_rf_interfaces[i] ==
1936a2968aaSIlan Elias 		    NCI_RF_INTERFACE_ISO_DEP) {
1942eb1dc10SIlan Elias 			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
195637d85a7SIlan Elias 			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
196637d85a7SIlan Elias 				NCI_DISC_MAP_MODE_LISTEN;
197637d85a7SIlan Elias 			cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP;
1982eb1dc10SIlan Elias 			(*num)++;
1996a2968aaSIlan Elias 		} else if (ndev->supported_rf_interfaces[i] ==
2006a2968aaSIlan Elias 			   NCI_RF_INTERFACE_NFC_DEP) {
2012eb1dc10SIlan Elias 			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
202637d85a7SIlan Elias 			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
203637d85a7SIlan Elias 				NCI_DISC_MAP_MODE_LISTEN;
204637d85a7SIlan Elias 			cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP;
2052eb1dc10SIlan Elias 			(*num)++;
2066a2968aaSIlan Elias 		}
2076a2968aaSIlan Elias 
2082eb1dc10SIlan Elias 		if (*num == NCI_MAX_NUM_MAPPING_CONFIGS)
2096a2968aaSIlan Elias 			break;
2106a2968aaSIlan Elias 	}
2116a2968aaSIlan Elias 
2126a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD,
213eb9bc6e9SSamuel Ortiz 		     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
2146a2968aaSIlan Elias }
2156a2968aaSIlan Elias 
2167e035230SIlan Elias struct nci_set_config_param {
2177e035230SIlan Elias 	__u8		id;
2187e035230SIlan Elias 	size_t		len;
219ddecf555SKrzysztof Kozlowski 	const __u8	*val;
2207e035230SIlan Elias };
2217e035230SIlan Elias 
nci_set_config_req(struct nci_dev * ndev,const void * opt)22235d7a6f1SKrzysztof Kozlowski static void nci_set_config_req(struct nci_dev *ndev, const void *opt)
2237e035230SIlan Elias {
22435d7a6f1SKrzysztof Kozlowski 	const struct nci_set_config_param *param = opt;
2257e035230SIlan Elias 	struct nci_core_set_config_cmd cmd;
2267e035230SIlan Elias 
2277e035230SIlan Elias 	BUG_ON(param->len > NCI_MAX_PARAM_LEN);
2287e035230SIlan Elias 
2297e035230SIlan Elias 	cmd.num_params = 1;
2307e035230SIlan Elias 	cmd.param.id = param->id;
2317e035230SIlan Elias 	cmd.param.len = param->len;
2327e035230SIlan Elias 	memcpy(cmd.param.val, param->val, param->len);
2337e035230SIlan Elias 
2347e035230SIlan Elias 	nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
2357e035230SIlan Elias }
2367e035230SIlan Elias 
237772dccf4SJulien Lefrique struct nci_rf_discover_param {
238772dccf4SJulien Lefrique 	__u32	im_protocols;
239772dccf4SJulien Lefrique 	__u32	tm_protocols;
240772dccf4SJulien Lefrique };
241772dccf4SJulien Lefrique 
nci_rf_discover_req(struct nci_dev * ndev,const void * opt)24235d7a6f1SKrzysztof Kozlowski static void nci_rf_discover_req(struct nci_dev *ndev, const void *opt)
2436a2968aaSIlan Elias {
24435d7a6f1SKrzysztof Kozlowski 	const struct nci_rf_discover_param *param = opt;
2456a2968aaSIlan Elias 	struct nci_rf_disc_cmd cmd;
2466a2968aaSIlan Elias 
2476a2968aaSIlan Elias 	cmd.num_disc_configs = 0;
2486a2968aaSIlan Elias 
2496a2968aaSIlan Elias 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
250772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
251772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_MIFARE_MASK ||
252772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_ISO14443_MASK ||
253772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
254637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
255637d85a7SIlan Elias 			NCI_NFC_A_PASSIVE_POLL_MODE;
2566a2968aaSIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
2576a2968aaSIlan Elias 		cmd.num_disc_configs++;
2586a2968aaSIlan Elias 	}
2596a2968aaSIlan Elias 
2606a2968aaSIlan Elias 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
261772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
262637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
263637d85a7SIlan Elias 			NCI_NFC_B_PASSIVE_POLL_MODE;
2646a2968aaSIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
2656a2968aaSIlan Elias 		cmd.num_disc_configs++;
2666a2968aaSIlan Elias 	}
2676a2968aaSIlan Elias 
2686a2968aaSIlan Elias 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
269772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_FELICA_MASK ||
270772dccf4SJulien Lefrique 	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
271637d85a7SIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
272637d85a7SIlan Elias 			NCI_NFC_F_PASSIVE_POLL_MODE;
2736a2968aaSIlan Elias 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
2746a2968aaSIlan Elias 		cmd.num_disc_configs++;
2756a2968aaSIlan Elias 	}
2766a2968aaSIlan Elias 
277cfdbeeafSVincent Cuissard 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
278772dccf4SJulien Lefrique 	    (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
279cfdbeeafSVincent Cuissard 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
280cfdbeeafSVincent Cuissard 			NCI_NFC_V_PASSIVE_POLL_MODE;
281cfdbeeafSVincent Cuissard 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
282cfdbeeafSVincent Cuissard 		cmd.num_disc_configs++;
283cfdbeeafSVincent Cuissard 	}
284cfdbeeafSVincent Cuissard 
285772dccf4SJulien Lefrique 	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
286772dccf4SJulien Lefrique 	    (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
287772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
288772dccf4SJulien Lefrique 			NCI_NFC_A_PASSIVE_LISTEN_MODE;
289772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
290772dccf4SJulien Lefrique 		cmd.num_disc_configs++;
291772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
292772dccf4SJulien Lefrique 			NCI_NFC_F_PASSIVE_LISTEN_MODE;
293772dccf4SJulien Lefrique 		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
294772dccf4SJulien Lefrique 		cmd.num_disc_configs++;
295772dccf4SJulien Lefrique 	}
296772dccf4SJulien Lefrique 
2976a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
2986a2968aaSIlan Elias 		     (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
2996a2968aaSIlan Elias 		     &cmd);
3006a2968aaSIlan Elias }
3016a2968aaSIlan Elias 
302019c4fbaSIlan Elias struct nci_rf_discover_select_param {
303019c4fbaSIlan Elias 	__u8	rf_discovery_id;
304019c4fbaSIlan Elias 	__u8	rf_protocol;
305019c4fbaSIlan Elias };
306019c4fbaSIlan Elias 
nci_rf_discover_select_req(struct nci_dev * ndev,const void * opt)30735d7a6f1SKrzysztof Kozlowski static void nci_rf_discover_select_req(struct nci_dev *ndev, const void *opt)
308019c4fbaSIlan Elias {
30935d7a6f1SKrzysztof Kozlowski 	const struct nci_rf_discover_select_param *param = opt;
310019c4fbaSIlan Elias 	struct nci_rf_discover_select_cmd cmd;
311019c4fbaSIlan Elias 
312019c4fbaSIlan Elias 	cmd.rf_discovery_id = param->rf_discovery_id;
313019c4fbaSIlan Elias 	cmd.rf_protocol = param->rf_protocol;
314019c4fbaSIlan Elias 
315019c4fbaSIlan Elias 	switch (cmd.rf_protocol) {
316019c4fbaSIlan Elias 	case NCI_RF_PROTOCOL_ISO_DEP:
317019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP;
318019c4fbaSIlan Elias 		break;
319019c4fbaSIlan Elias 
320019c4fbaSIlan Elias 	case NCI_RF_PROTOCOL_NFC_DEP:
321019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP;
322019c4fbaSIlan Elias 		break;
323019c4fbaSIlan Elias 
324019c4fbaSIlan Elias 	default:
325019c4fbaSIlan Elias 		cmd.rf_interface = NCI_RF_INTERFACE_FRAME;
326019c4fbaSIlan Elias 		break;
327019c4fbaSIlan Elias 	}
328019c4fbaSIlan Elias 
329019c4fbaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD,
330eb9bc6e9SSamuel Ortiz 		     sizeof(struct nci_rf_discover_select_cmd), &cmd);
331019c4fbaSIlan Elias }
332019c4fbaSIlan Elias 
nci_rf_deactivate_req(struct nci_dev * ndev,const void * opt)33335d7a6f1SKrzysztof Kozlowski static void nci_rf_deactivate_req(struct nci_dev *ndev, const void *opt)
3346a2968aaSIlan Elias {
3356a2968aaSIlan Elias 	struct nci_rf_deactivate_cmd cmd;
3366a2968aaSIlan Elias 
33735d7a6f1SKrzysztof Kozlowski 	cmd.type = (unsigned long)opt;
3386a2968aaSIlan Elias 
3396a2968aaSIlan Elias 	nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
340eb9bc6e9SSamuel Ortiz 		     sizeof(struct nci_rf_deactivate_cmd), &cmd);
3416a2968aaSIlan Elias }
3426a2968aaSIlan Elias 
3437bc4824eSRobert Dolca struct nci_cmd_param {
344759afb8dSChristophe Ricard 	__u16 opcode;
345759afb8dSChristophe Ricard 	size_t len;
346ddecf555SKrzysztof Kozlowski 	const __u8 *payload;
347759afb8dSChristophe Ricard };
348759afb8dSChristophe Ricard 
nci_generic_req(struct nci_dev * ndev,const void * opt)34935d7a6f1SKrzysztof Kozlowski static void nci_generic_req(struct nci_dev *ndev, const void *opt)
350759afb8dSChristophe Ricard {
35135d7a6f1SKrzysztof Kozlowski 	const struct nci_cmd_param *param = opt;
352759afb8dSChristophe Ricard 
353759afb8dSChristophe Ricard 	nci_send_cmd(ndev, param->opcode, param->len, param->payload);
354759afb8dSChristophe Ricard }
355759afb8dSChristophe Ricard 
nci_prop_cmd(struct nci_dev * ndev,__u8 oid,size_t len,const __u8 * payload)356ddecf555SKrzysztof Kozlowski int nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, const __u8 *payload)
357759afb8dSChristophe Ricard {
3587bc4824eSRobert Dolca 	struct nci_cmd_param param;
359759afb8dSChristophe Ricard 
360759afb8dSChristophe Ricard 	param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
361759afb8dSChristophe Ricard 	param.len = len;
362759afb8dSChristophe Ricard 	param.payload = payload;
363759afb8dSChristophe Ricard 
36435d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_generic_req, &param,
365759afb8dSChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
366759afb8dSChristophe Ricard }
367759afb8dSChristophe Ricard EXPORT_SYMBOL(nci_prop_cmd);
368759afb8dSChristophe Ricard 
nci_core_cmd(struct nci_dev * ndev,__u16 opcode,size_t len,const __u8 * payload)369ddecf555SKrzysztof Kozlowski int nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len,
370ddecf555SKrzysztof Kozlowski 		 const __u8 *payload)
3717bc4824eSRobert Dolca {
3727bc4824eSRobert Dolca 	struct nci_cmd_param param;
3737bc4824eSRobert Dolca 
3747bc4824eSRobert Dolca 	param.opcode = opcode;
3757bc4824eSRobert Dolca 	param.len = len;
3767bc4824eSRobert Dolca 	param.payload = payload;
3777bc4824eSRobert Dolca 
37835d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_generic_req, &param,
3797bc4824eSRobert Dolca 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
3807bc4824eSRobert Dolca }
3817bc4824eSRobert Dolca EXPORT_SYMBOL(nci_core_cmd);
3827bc4824eSRobert Dolca 
nci_core_reset(struct nci_dev * ndev)383025a0cb8SRobert Baldyga int nci_core_reset(struct nci_dev *ndev)
384025a0cb8SRobert Baldyga {
38535d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_reset_req, (void *)0,
386025a0cb8SRobert Baldyga 			     msecs_to_jiffies(NCI_RESET_TIMEOUT));
387025a0cb8SRobert Baldyga }
388025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_reset);
389025a0cb8SRobert Baldyga 
nci_core_init(struct nci_dev * ndev)390025a0cb8SRobert Baldyga int nci_core_init(struct nci_dev *ndev)
391025a0cb8SRobert Baldyga {
39235d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_init_req, (void *)0,
393025a0cb8SRobert Baldyga 			     msecs_to_jiffies(NCI_INIT_TIMEOUT));
394025a0cb8SRobert Baldyga }
395025a0cb8SRobert Baldyga EXPORT_SYMBOL(nci_core_init);
396025a0cb8SRobert Baldyga 
3971c53855fSChristophe Ricard struct nci_loopback_data {
3981c53855fSChristophe Ricard 	u8 conn_id;
3991c53855fSChristophe Ricard 	struct sk_buff *data;
4001c53855fSChristophe Ricard };
4011c53855fSChristophe Ricard 
nci_send_data_req(struct nci_dev * ndev,const void * opt)40235d7a6f1SKrzysztof Kozlowski static void nci_send_data_req(struct nci_dev *ndev, const void *opt)
4031c53855fSChristophe Ricard {
40435d7a6f1SKrzysztof Kozlowski 	const struct nci_loopback_data *data = opt;
4051c53855fSChristophe Ricard 
4061c53855fSChristophe Ricard 	nci_send_data(ndev, data->conn_id, data->data);
4071c53855fSChristophe Ricard }
4081c53855fSChristophe Ricard 
nci_nfcc_loopback_cb(void * context,struct sk_buff * skb,int err)4091c53855fSChristophe Ricard static void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err)
4101c53855fSChristophe Ricard {
4111c53855fSChristophe Ricard 	struct nci_dev *ndev = (struct nci_dev *)context;
4121c53855fSChristophe Ricard 	struct nci_conn_info *conn_info;
4131c53855fSChristophe Ricard 
4141c53855fSChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
4151c53855fSChristophe Ricard 	if (!conn_info) {
4161c53855fSChristophe Ricard 		nci_req_complete(ndev, NCI_STATUS_REJECTED);
4171c53855fSChristophe Ricard 		return;
4181c53855fSChristophe Ricard 	}
4191c53855fSChristophe Ricard 
4201c53855fSChristophe Ricard 	conn_info->rx_skb = skb;
4211c53855fSChristophe Ricard 
4221c53855fSChristophe Ricard 	nci_req_complete(ndev, NCI_STATUS_OK);
4231c53855fSChristophe Ricard }
4241c53855fSChristophe Ricard 
nci_nfcc_loopback(struct nci_dev * ndev,const void * data,size_t data_len,struct sk_buff ** resp)425ddecf555SKrzysztof Kozlowski int nci_nfcc_loopback(struct nci_dev *ndev, const void *data, size_t data_len,
4261c53855fSChristophe Ricard 		      struct sk_buff **resp)
4271c53855fSChristophe Ricard {
4281c53855fSChristophe Ricard 	int r;
4291c53855fSChristophe Ricard 	struct nci_loopback_data loopback_data;
4301c53855fSChristophe Ricard 	struct nci_conn_info *conn_info;
4311c53855fSChristophe Ricard 	struct sk_buff *skb;
4321c53855fSChristophe Ricard 	int conn_id = nci_get_conn_info_by_dest_type_params(ndev,
4331c53855fSChristophe Ricard 					NCI_DESTINATION_NFCC_LOOPBACK, NULL);
4341c53855fSChristophe Ricard 
4351c53855fSChristophe Ricard 	if (conn_id < 0) {
4361c53855fSChristophe Ricard 		r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK,
4371c53855fSChristophe Ricard 					 0, 0, NULL);
4381c53855fSChristophe Ricard 		if (r != NCI_STATUS_OK)
4391c53855fSChristophe Ricard 			return r;
4401c53855fSChristophe Ricard 
4411c53855fSChristophe Ricard 		conn_id = nci_get_conn_info_by_dest_type_params(ndev,
4421c53855fSChristophe Ricard 					NCI_DESTINATION_NFCC_LOOPBACK,
4431c53855fSChristophe Ricard 					NULL);
4441c53855fSChristophe Ricard 	}
4451c53855fSChristophe Ricard 
4461c53855fSChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
4471c53855fSChristophe Ricard 	if (!conn_info)
4481c53855fSChristophe Ricard 		return -EPROTO;
4491c53855fSChristophe Ricard 
4501c53855fSChristophe Ricard 	/* store cb and context to be used on receiving data */
4511c53855fSChristophe Ricard 	conn_info->data_exchange_cb = nci_nfcc_loopback_cb;
4521c53855fSChristophe Ricard 	conn_info->data_exchange_cb_context = ndev;
4531c53855fSChristophe Ricard 
4541c53855fSChristophe Ricard 	skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL);
4551c53855fSChristophe Ricard 	if (!skb)
4561c53855fSChristophe Ricard 		return -ENOMEM;
4571c53855fSChristophe Ricard 
4581c53855fSChristophe Ricard 	skb_reserve(skb, NCI_DATA_HDR_SIZE);
45959ae1d12SJohannes Berg 	skb_put_data(skb, data, data_len);
4601c53855fSChristophe Ricard 
4611c53855fSChristophe Ricard 	loopback_data.conn_id = conn_id;
4621c53855fSChristophe Ricard 	loopback_data.data = skb;
4631c53855fSChristophe Ricard 
4641c53855fSChristophe Ricard 	ndev->cur_conn_id = conn_id;
46535d7a6f1SKrzysztof Kozlowski 	r = nci_request(ndev, nci_send_data_req, &loopback_data,
4661c53855fSChristophe Ricard 			msecs_to_jiffies(NCI_DATA_TIMEOUT));
4671c53855fSChristophe Ricard 	if (r == NCI_STATUS_OK && resp)
4681c53855fSChristophe Ricard 		*resp = conn_info->rx_skb;
4691c53855fSChristophe Ricard 
4701c53855fSChristophe Ricard 	return r;
4711c53855fSChristophe Ricard }
4721c53855fSChristophe Ricard EXPORT_SYMBOL(nci_nfcc_loopback);
4731c53855fSChristophe Ricard 
nci_open_device(struct nci_dev * ndev)4746a2968aaSIlan Elias static int nci_open_device(struct nci_dev *ndev)
4756a2968aaSIlan Elias {
4766a2968aaSIlan Elias 	int rc = 0;
4776a2968aaSIlan Elias 
4786a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
4796a2968aaSIlan Elias 
48048b71a9eSLin Ma 	if (test_bit(NCI_UNREG, &ndev->flags)) {
48148b71a9eSLin Ma 		rc = -ENODEV;
48248b71a9eSLin Ma 		goto done;
48348b71a9eSLin Ma 	}
48448b71a9eSLin Ma 
4856a2968aaSIlan Elias 	if (test_bit(NCI_UP, &ndev->flags)) {
4866a2968aaSIlan Elias 		rc = -EALREADY;
4876a2968aaSIlan Elias 		goto done;
4886a2968aaSIlan Elias 	}
4896a2968aaSIlan Elias 
4906a2968aaSIlan Elias 	if (ndev->ops->open(ndev)) {
4916a2968aaSIlan Elias 		rc = -EIO;
4926a2968aaSIlan Elias 		goto done;
4936a2968aaSIlan Elias 	}
4946a2968aaSIlan Elias 
4956a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
4966a2968aaSIlan Elias 
4976a2968aaSIlan Elias 	set_bit(NCI_INIT, &ndev->flags);
4986a2968aaSIlan Elias 
499c39daeeeSChristophe Ricard 	if (ndev->ops->init)
500c39daeeeSChristophe Ricard 		rc = ndev->ops->init(ndev);
501c39daeeeSChristophe Ricard 
502c39daeeeSChristophe Ricard 	if (!rc) {
50335d7a6f1SKrzysztof Kozlowski 		rc = __nci_request(ndev, nci_reset_req, (void *)0,
5046a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_RESET_TIMEOUT));
505c39daeeeSChristophe Ricard 	}
5066a2968aaSIlan Elias 
50781859ab8SChristophe Ricard 	if (!rc && ndev->ops->setup) {
50881859ab8SChristophe Ricard 		rc = ndev->ops->setup(ndev);
50981859ab8SChristophe Ricard 	}
51086e8586eSAmitkumar Karwar 
5116a2968aaSIlan Elias 	if (!rc) {
512bcd684aaSBongsu Jeon 		struct nci_core_init_v2_cmd nci_init_v2_cmd = {
513bcd684aaSBongsu Jeon 			.feature1 = NCI_FEATURE_DISABLE,
514bcd684aaSBongsu Jeon 			.feature2 = NCI_FEATURE_DISABLE
515bcd684aaSBongsu Jeon 		};
51635d7a6f1SKrzysztof Kozlowski 		const void *opt = NULL;
517bcd684aaSBongsu Jeon 
5184964e5a1SBongsu Jeon 		if (ndev->nci_ver & NCI_VER_2_MASK)
51935d7a6f1SKrzysztof Kozlowski 			opt = &nci_init_v2_cmd;
520bcd684aaSBongsu Jeon 
521bcd684aaSBongsu Jeon 		rc = __nci_request(ndev, nci_init_req, opt,
5226a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
5236a2968aaSIlan Elias 	}
5246a2968aaSIlan Elias 
525e4dbd625SRobert Dolca 	if (!rc && ndev->ops->post_setup)
526fdf79bd4SRobert Baldyga 		rc = ndev->ops->post_setup(ndev);
527fdf79bd4SRobert Baldyga 
5286a2968aaSIlan Elias 	if (!rc) {
52935d7a6f1SKrzysztof Kozlowski 		rc = __nci_request(ndev, nci_init_complete_req, (void *)0,
5306a2968aaSIlan Elias 				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
5316a2968aaSIlan Elias 	}
5326a2968aaSIlan Elias 
5336a2968aaSIlan Elias 	clear_bit(NCI_INIT, &ndev->flags);
5346a2968aaSIlan Elias 
5356a2968aaSIlan Elias 	if (!rc) {
5366a2968aaSIlan Elias 		set_bit(NCI_UP, &ndev->flags);
537019c4fbaSIlan Elias 		nci_clear_target_list(ndev);
5388939e47fSIlan Elias 		atomic_set(&ndev->state, NCI_IDLE);
5396a2968aaSIlan Elias 	} else {
5406a2968aaSIlan Elias 		/* Init failed, cleanup */
5416a2968aaSIlan Elias 		skb_queue_purge(&ndev->cmd_q);
5426a2968aaSIlan Elias 		skb_queue_purge(&ndev->rx_q);
5436a2968aaSIlan Elias 		skb_queue_purge(&ndev->tx_q);
5446a2968aaSIlan Elias 
5456a2968aaSIlan Elias 		ndev->ops->close(ndev);
5460ad6bdedSLin Ma 		ndev->flags &= BIT(NCI_UNREG);
5476a2968aaSIlan Elias 	}
5486a2968aaSIlan Elias 
5496a2968aaSIlan Elias done:
5506a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
5516a2968aaSIlan Elias 	return rc;
5526a2968aaSIlan Elias }
5536a2968aaSIlan Elias 
nci_close_device(struct nci_dev * ndev)5546a2968aaSIlan Elias static int nci_close_device(struct nci_dev *ndev)
5556a2968aaSIlan Elias {
5566a2968aaSIlan Elias 	nci_req_cancel(ndev, ENODEV);
55748b71a9eSLin Ma 
55848b71a9eSLin Ma 	/* This mutex needs to be held as a barrier for
55948b71a9eSLin Ma 	 * caller nci_unregister_device
56048b71a9eSLin Ma 	 */
5616a2968aaSIlan Elias 	mutex_lock(&ndev->req_lock);
5626a2968aaSIlan Elias 
5636a2968aaSIlan Elias 	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
564ef27324eSLin Ma 		/* Need to flush the cmd wq in case
565ef27324eSLin Ma 		 * there is a queued/running cmd_work
566ef27324eSLin Ma 		 */
567ef27324eSLin Ma 		flush_workqueue(ndev->cmd_wq);
5686a2968aaSIlan Elias 		del_timer_sync(&ndev->cmd_timer);
569c4bf98b2SIlan Elias 		del_timer_sync(&ndev->data_timer);
5706a2968aaSIlan Elias 		mutex_unlock(&ndev->req_lock);
5716a2968aaSIlan Elias 		return 0;
5726a2968aaSIlan Elias 	}
5736a2968aaSIlan Elias 
5746a2968aaSIlan Elias 	/* Drop RX and TX queues */
5756a2968aaSIlan Elias 	skb_queue_purge(&ndev->rx_q);
5766a2968aaSIlan Elias 	skb_queue_purge(&ndev->tx_q);
5776a2968aaSIlan Elias 
5786a2968aaSIlan Elias 	/* Flush RX and TX wq */
5796a2968aaSIlan Elias 	flush_workqueue(ndev->rx_wq);
5806a2968aaSIlan Elias 	flush_workqueue(ndev->tx_wq);
5816a2968aaSIlan Elias 
5826a2968aaSIlan Elias 	/* Reset device */
5836a2968aaSIlan Elias 	skb_queue_purge(&ndev->cmd_q);
5846a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
5856a2968aaSIlan Elias 
5866a2968aaSIlan Elias 	set_bit(NCI_INIT, &ndev->flags);
58735d7a6f1SKrzysztof Kozlowski 	__nci_request(ndev, nci_reset_req, (void *)0,
5886a2968aaSIlan Elias 		      msecs_to_jiffies(NCI_RESET_TIMEOUT));
5890e70cba7SChristophe Ricard 
5900e70cba7SChristophe Ricard 	/* After this point our queues are empty
5910e70cba7SChristophe Ricard 	 * and no works are scheduled.
5920e70cba7SChristophe Ricard 	 */
5930e70cba7SChristophe Ricard 	ndev->ops->close(ndev);
5940e70cba7SChristophe Ricard 
5956a2968aaSIlan Elias 	clear_bit(NCI_INIT, &ndev->flags);
5966a2968aaSIlan Elias 
5976a2968aaSIlan Elias 	/* Flush cmd wq */
5986a2968aaSIlan Elias 	flush_workqueue(ndev->cmd_wq);
5996a2968aaSIlan Elias 
600f011539eSBongsu Jeon 	del_timer_sync(&ndev->cmd_timer);
601f011539eSBongsu Jeon 
60248b71a9eSLin Ma 	/* Clear flags except NCI_UNREG */
60348b71a9eSLin Ma 	ndev->flags &= BIT(NCI_UNREG);
6046a2968aaSIlan Elias 
6056a2968aaSIlan Elias 	mutex_unlock(&ndev->req_lock);
6066a2968aaSIlan Elias 
6076a2968aaSIlan Elias 	return 0;
6086a2968aaSIlan Elias }
6096a2968aaSIlan Elias 
6106a2968aaSIlan Elias /* NCI command timer function */
nci_cmd_timer(struct timer_list * t)611e99e88a9SKees Cook static void nci_cmd_timer(struct timer_list *t)
6126a2968aaSIlan Elias {
613e99e88a9SKees Cook 	struct nci_dev *ndev = from_timer(ndev, t, cmd_timer);
6146a2968aaSIlan Elias 
6156a2968aaSIlan Elias 	atomic_set(&ndev->cmd_cnt, 1);
6166a2968aaSIlan Elias 	queue_work(ndev->cmd_wq, &ndev->cmd_work);
6176a2968aaSIlan Elias }
6186a2968aaSIlan Elias 
619c4bf98b2SIlan Elias /* NCI data exchange timer function */
nci_data_timer(struct timer_list * t)620e99e88a9SKees Cook static void nci_data_timer(struct timer_list *t)
621c4bf98b2SIlan Elias {
622e99e88a9SKees Cook 	struct nci_dev *ndev = from_timer(ndev, t, data_timer);
623c4bf98b2SIlan Elias 
624c4bf98b2SIlan Elias 	set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
625c4bf98b2SIlan Elias 	queue_work(ndev->rx_wq, &ndev->rx_work);
626c4bf98b2SIlan Elias }
627c4bf98b2SIlan Elias 
nci_dev_up(struct nfc_dev * nfc_dev)6286a2968aaSIlan Elias static int nci_dev_up(struct nfc_dev *nfc_dev)
6296a2968aaSIlan Elias {
6306a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
6316a2968aaSIlan Elias 
6326a2968aaSIlan Elias 	return nci_open_device(ndev);
6336a2968aaSIlan Elias }
6346a2968aaSIlan Elias 
nci_dev_down(struct nfc_dev * nfc_dev)6356a2968aaSIlan Elias static int nci_dev_down(struct nfc_dev *nfc_dev)
6366a2968aaSIlan Elias {
6376a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
6386a2968aaSIlan Elias 
6396a2968aaSIlan Elias 	return nci_close_device(ndev);
6406a2968aaSIlan Elias }
6416a2968aaSIlan Elias 
nci_set_config(struct nci_dev * ndev,__u8 id,size_t len,const __u8 * val)642ddecf555SKrzysztof Kozlowski int nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, const __u8 *val)
64322c15bf3SAmitkumar Karwar {
64422c15bf3SAmitkumar Karwar 	struct nci_set_config_param param;
64522c15bf3SAmitkumar Karwar 
64622c15bf3SAmitkumar Karwar 	if (!val || !len)
64722c15bf3SAmitkumar Karwar 		return 0;
64822c15bf3SAmitkumar Karwar 
64922c15bf3SAmitkumar Karwar 	param.id = id;
65022c15bf3SAmitkumar Karwar 	param.len = len;
65122c15bf3SAmitkumar Karwar 	param.val = val;
65222c15bf3SAmitkumar Karwar 
65335d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_set_config_req, &param,
65422c15bf3SAmitkumar Karwar 			     msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
65522c15bf3SAmitkumar Karwar }
65622c15bf3SAmitkumar Karwar EXPORT_SYMBOL(nci_set_config);
65722c15bf3SAmitkumar Karwar 
nci_nfcee_discover_req(struct nci_dev * ndev,const void * opt)65835d7a6f1SKrzysztof Kozlowski static void nci_nfcee_discover_req(struct nci_dev *ndev, const void *opt)
659af9c8aa6SChristophe Ricard {
660af9c8aa6SChristophe Ricard 	struct nci_nfcee_discover_cmd cmd;
66135d7a6f1SKrzysztof Kozlowski 	__u8 action = (unsigned long)opt;
662af9c8aa6SChristophe Ricard 
663af9c8aa6SChristophe Ricard 	cmd.discovery_action = action;
664af9c8aa6SChristophe Ricard 
665af9c8aa6SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd);
666af9c8aa6SChristophe Ricard }
667af9c8aa6SChristophe Ricard 
nci_nfcee_discover(struct nci_dev * ndev,u8 action)668af9c8aa6SChristophe Ricard int nci_nfcee_discover(struct nci_dev *ndev, u8 action)
669af9c8aa6SChristophe Ricard {
67035d7a6f1SKrzysztof Kozlowski 	unsigned long opt = action;
67135d7a6f1SKrzysztof Kozlowski 
67235d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_nfcee_discover_req, (void *)opt,
673af9c8aa6SChristophe Ricard 				msecs_to_jiffies(NCI_CMD_TIMEOUT));
674af9c8aa6SChristophe Ricard }
675af9c8aa6SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_discover);
676af9c8aa6SChristophe Ricard 
nci_nfcee_mode_set_req(struct nci_dev * ndev,const void * opt)67735d7a6f1SKrzysztof Kozlowski static void nci_nfcee_mode_set_req(struct nci_dev *ndev, const void *opt)
678f7f793f3SChristophe Ricard {
67935d7a6f1SKrzysztof Kozlowski 	const struct nci_nfcee_mode_set_cmd *cmd = opt;
680f7f793f3SChristophe Ricard 
681f7f793f3SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD,
682f7f793f3SChristophe Ricard 		     sizeof(struct nci_nfcee_mode_set_cmd), cmd);
683f7f793f3SChristophe Ricard }
684f7f793f3SChristophe Ricard 
nci_nfcee_mode_set(struct nci_dev * ndev,u8 nfcee_id,u8 nfcee_mode)685f7f793f3SChristophe Ricard int nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
686f7f793f3SChristophe Ricard {
687f7f793f3SChristophe Ricard 	struct nci_nfcee_mode_set_cmd cmd;
688f7f793f3SChristophe Ricard 
689f7f793f3SChristophe Ricard 	cmd.nfcee_id = nfcee_id;
690f7f793f3SChristophe Ricard 	cmd.nfcee_mode = nfcee_mode;
691f7f793f3SChristophe Ricard 
69235d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_nfcee_mode_set_req, &cmd,
693f7f793f3SChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
694f7f793f3SChristophe Ricard }
695f7f793f3SChristophe Ricard EXPORT_SYMBOL(nci_nfcee_mode_set);
696f7f793f3SChristophe Ricard 
nci_core_conn_create_req(struct nci_dev * ndev,const void * opt)69735d7a6f1SKrzysztof Kozlowski static void nci_core_conn_create_req(struct nci_dev *ndev, const void *opt)
698736bb957SChristophe Ricard {
69935d7a6f1SKrzysztof Kozlowski 	const struct core_conn_create_data *data = opt;
700736bb957SChristophe Ricard 
701b16ae716SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd);
702736bb957SChristophe Ricard }
703736bb957SChristophe Ricard 
nci_core_conn_create(struct nci_dev * ndev,u8 destination_type,u8 number_destination_params,size_t params_len,const struct core_conn_create_dest_spec_params * params)704b16ae716SChristophe Ricard int nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
705b16ae716SChristophe Ricard 			 u8 number_destination_params,
706b16ae716SChristophe Ricard 			 size_t params_len,
707ddecf555SKrzysztof Kozlowski 			 const struct core_conn_create_dest_spec_params *params)
708736bb957SChristophe Ricard {
709b16ae716SChristophe Ricard 	int r;
710b16ae716SChristophe Ricard 	struct nci_core_conn_create_cmd *cmd;
711b16ae716SChristophe Ricard 	struct core_conn_create_data data;
712b16ae716SChristophe Ricard 
713b16ae716SChristophe Ricard 	data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
714b16ae716SChristophe Ricard 	cmd = kzalloc(data.length, GFP_KERNEL);
715b16ae716SChristophe Ricard 	if (!cmd)
716b16ae716SChristophe Ricard 		return -ENOMEM;
717b16ae716SChristophe Ricard 
718b16ae716SChristophe Ricard 	cmd->destination_type = destination_type;
719b16ae716SChristophe Ricard 	cmd->number_destination_params = number_destination_params;
720b16ae716SChristophe Ricard 
721b16ae716SChristophe Ricard 	data.cmd = cmd;
722caa575a8SRobert Dolca 
72318836029SChristophe Ricard 	if (params) {
72418836029SChristophe Ricard 		memcpy(cmd->params, params, params_len);
725caa575a8SRobert Dolca 		if (params->length > 0)
7269b8d1a4cSChristophe Ricard 			memcpy(&ndev->cur_params,
7279b8d1a4cSChristophe Ricard 			       &params->value[DEST_SPEC_PARAMS_ID_INDEX],
7289b8d1a4cSChristophe Ricard 			       sizeof(struct dest_spec_params));
729caa575a8SRobert Dolca 		else
7309b8d1a4cSChristophe Ricard 			ndev->cur_params.id = 0;
73118836029SChristophe Ricard 	} else {
7329b8d1a4cSChristophe Ricard 		ndev->cur_params.id = 0;
73318836029SChristophe Ricard 	}
7349b8d1a4cSChristophe Ricard 	ndev->cur_dest_type = destination_type;
735b16ae716SChristophe Ricard 
73635d7a6f1SKrzysztof Kozlowski 	r = __nci_request(ndev, nci_core_conn_create_req, &data,
737736bb957SChristophe Ricard 			  msecs_to_jiffies(NCI_CMD_TIMEOUT));
738b16ae716SChristophe Ricard 	kfree(cmd);
739b16ae716SChristophe Ricard 	return r;
740736bb957SChristophe Ricard }
741736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_create);
742736bb957SChristophe Ricard 
nci_core_conn_close_req(struct nci_dev * ndev,const void * opt)74335d7a6f1SKrzysztof Kozlowski static void nci_core_conn_close_req(struct nci_dev *ndev, const void *opt)
744736bb957SChristophe Ricard {
74535d7a6f1SKrzysztof Kozlowski 	__u8 conn_id = (unsigned long)opt;
746736bb957SChristophe Ricard 
747736bb957SChristophe Ricard 	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id);
748736bb957SChristophe Ricard }
749736bb957SChristophe Ricard 
nci_core_conn_close(struct nci_dev * ndev,u8 conn_id)750736bb957SChristophe Ricard int nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
751736bb957SChristophe Ricard {
75235d7a6f1SKrzysztof Kozlowski 	unsigned long opt = conn_id;
75335d7a6f1SKrzysztof Kozlowski 
754de5ea851SChristophe Ricard 	ndev->cur_conn_id = conn_id;
75535d7a6f1SKrzysztof Kozlowski 	return __nci_request(ndev, nci_core_conn_close_req, (void *)opt,
756736bb957SChristophe Ricard 			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
757736bb957SChristophe Ricard }
758736bb957SChristophe Ricard EXPORT_SYMBOL(nci_core_conn_close);
759736bb957SChristophe Ricard 
nci_set_local_general_bytes(struct nfc_dev * nfc_dev)7607e035230SIlan Elias static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
7617e035230SIlan Elias {
7627e035230SIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
7637e035230SIlan Elias 	struct nci_set_config_param param;
764529ee066SJulien Lefrique 	int rc;
7657e035230SIlan Elias 
7667e035230SIlan Elias 	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
7677e035230SIlan Elias 	if ((param.val == NULL) || (param.len == 0))
768f9fc36f4SSzymon Janc 		return 0;
7697e035230SIlan Elias 
770460d8f97SSzymon Janc 	if (param.len > NFC_MAX_GT_LEN)
7717e035230SIlan Elias 		return -EINVAL;
7727e035230SIlan Elias 
7737e035230SIlan Elias 	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
7747e035230SIlan Elias 
77535d7a6f1SKrzysztof Kozlowski 	rc = nci_request(ndev, nci_set_config_req, &param,
776529ee066SJulien Lefrique 			 msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
777529ee066SJulien Lefrique 	if (rc)
778529ee066SJulien Lefrique 		return rc;
779529ee066SJulien Lefrique 
780529ee066SJulien Lefrique 	param.id = NCI_LN_ATR_RES_GEN_BYTES;
781529ee066SJulien Lefrique 
78235d7a6f1SKrzysztof Kozlowski 	return nci_request(ndev, nci_set_config_req, &param,
7837e035230SIlan Elias 			   msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
7847e035230SIlan Elias }
7857e035230SIlan Elias 
nci_set_listen_parameters(struct nfc_dev * nfc_dev)78690d78c13SJulien Lefrique static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
78790d78c13SJulien Lefrique {
78890d78c13SJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
78990d78c13SJulien Lefrique 	int rc;
79090d78c13SJulien Lefrique 	__u8 val;
79190d78c13SJulien Lefrique 
79290d78c13SJulien Lefrique 	val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
79390d78c13SJulien Lefrique 
79490d78c13SJulien Lefrique 	rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
79590d78c13SJulien Lefrique 	if (rc)
79690d78c13SJulien Lefrique 		return rc;
79790d78c13SJulien Lefrique 
79890d78c13SJulien Lefrique 	val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
79990d78c13SJulien Lefrique 
80090d78c13SJulien Lefrique 	rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
80190d78c13SJulien Lefrique 	if (rc)
80290d78c13SJulien Lefrique 		return rc;
80390d78c13SJulien Lefrique 
80490d78c13SJulien Lefrique 	val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
80590d78c13SJulien Lefrique 
80690d78c13SJulien Lefrique 	return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
80790d78c13SJulien Lefrique }
80890d78c13SJulien Lefrique 
nci_start_poll(struct nfc_dev * nfc_dev,__u32 im_protocols,__u32 tm_protocols)809fe7c5800SSamuel Ortiz static int nci_start_poll(struct nfc_dev *nfc_dev,
810fe7c5800SSamuel Ortiz 			  __u32 im_protocols, __u32 tm_protocols)
8116a2968aaSIlan Elias {
8126a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
813772dccf4SJulien Lefrique 	struct nci_rf_discover_param param;
8146a2968aaSIlan Elias 	int rc;
8156a2968aaSIlan Elias 
816019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
817019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) {
818ed1e0ad8SJoe Perches 		pr_err("unable to start poll, since poll is already active\n");
8196a2968aaSIlan Elias 		return -EBUSY;
8206a2968aaSIlan Elias 	}
8216a2968aaSIlan Elias 
822de054799SIlan Elias 	if (ndev->target_active_prot) {
823ed1e0ad8SJoe Perches 		pr_err("there is an active target\n");
824de054799SIlan Elias 		return -EBUSY;
825de054799SIlan Elias 	}
826de054799SIlan Elias 
827019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) ||
828019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
829019c4fbaSIlan Elias 		pr_debug("target active or w4 select, implicitly deactivate\n");
8306a2968aaSIlan Elias 
8319295b5b5SChristophe Ricard 		rc = nci_request(ndev, nci_rf_deactivate_req,
83235d7a6f1SKrzysztof Kozlowski 				 (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE,
8336a2968aaSIlan Elias 				 msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
8346a2968aaSIlan Elias 		if (rc)
8356a2968aaSIlan Elias 			return -EBUSY;
8366a2968aaSIlan Elias 	}
8376a2968aaSIlan Elias 
838529ee066SJulien Lefrique 	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
8397e035230SIlan Elias 		rc = nci_set_local_general_bytes(nfc_dev);
8407e035230SIlan Elias 		if (rc) {
8417e035230SIlan Elias 			pr_err("failed to set local general bytes\n");
8427e035230SIlan Elias 			return rc;
8437e035230SIlan Elias 		}
8447e035230SIlan Elias 	}
8457e035230SIlan Elias 
84690d78c13SJulien Lefrique 	if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
84790d78c13SJulien Lefrique 		rc = nci_set_listen_parameters(nfc_dev);
84890d78c13SJulien Lefrique 		if (rc)
84990d78c13SJulien Lefrique 			pr_err("failed to set listen parameters\n");
85090d78c13SJulien Lefrique 	}
85190d78c13SJulien Lefrique 
852772dccf4SJulien Lefrique 	param.im_protocols = im_protocols;
853772dccf4SJulien Lefrique 	param.tm_protocols = tm_protocols;
85435d7a6f1SKrzysztof Kozlowski 	rc = nci_request(ndev, nci_rf_discover_req, &param,
8556a2968aaSIlan Elias 			 msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
8566a2968aaSIlan Elias 
8576a2968aaSIlan Elias 	if (!rc)
858fe7c5800SSamuel Ortiz 		ndev->poll_prots = im_protocols;
8596a2968aaSIlan Elias 
8606a2968aaSIlan Elias 	return rc;
8616a2968aaSIlan Elias }
8626a2968aaSIlan Elias 
nci_stop_poll(struct nfc_dev * nfc_dev)8636a2968aaSIlan Elias static void nci_stop_poll(struct nfc_dev *nfc_dev)
8646a2968aaSIlan Elias {
8656a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
8666a2968aaSIlan Elias 
867019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) != NCI_DISCOVERY) &&
868019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) {
869ed1e0ad8SJoe Perches 		pr_err("unable to stop poll, since poll is not active\n");
8706a2968aaSIlan Elias 		return;
8716a2968aaSIlan Elias 	}
8726a2968aaSIlan Elias 
87335d7a6f1SKrzysztof Kozlowski 	nci_request(ndev, nci_rf_deactivate_req,
87435d7a6f1SKrzysztof Kozlowski 		    (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE,
8756a2968aaSIlan Elias 		    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
8766a2968aaSIlan Elias }
8776a2968aaSIlan Elias 
nci_activate_target(struct nfc_dev * nfc_dev,struct nfc_target * target,__u32 protocol)87890099433SEric Lapuyade static int nci_activate_target(struct nfc_dev *nfc_dev,
87990099433SEric Lapuyade 			       struct nfc_target *target, __u32 protocol)
8806a2968aaSIlan Elias {
8816a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
882019c4fbaSIlan Elias 	struct nci_rf_discover_select_param param;
883ddecf555SKrzysztof Kozlowski 	const struct nfc_target *nci_target = NULL;
884019c4fbaSIlan Elias 	int i;
885019c4fbaSIlan Elias 	int rc = 0;
8866a2968aaSIlan Elias 
88790099433SEric Lapuyade 	pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol);
8886a2968aaSIlan Elias 
889019c4fbaSIlan Elias 	if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) &&
890019c4fbaSIlan Elias 	    (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
891ed1e0ad8SJoe Perches 		pr_err("there is no available target to activate\n");
8926a2968aaSIlan Elias 		return -EINVAL;
8936a2968aaSIlan Elias 	}
8946a2968aaSIlan Elias 
8956a2968aaSIlan Elias 	if (ndev->target_active_prot) {
896ed1e0ad8SJoe Perches 		pr_err("there is already an active target\n");
8976a2968aaSIlan Elias 		return -EBUSY;
8986a2968aaSIlan Elias 	}
8996a2968aaSIlan Elias 
900019c4fbaSIlan Elias 	for (i = 0; i < ndev->n_targets; i++) {
90190099433SEric Lapuyade 		if (ndev->targets[i].idx == target->idx) {
90290099433SEric Lapuyade 			nci_target = &ndev->targets[i];
903019c4fbaSIlan Elias 			break;
904019c4fbaSIlan Elias 		}
905019c4fbaSIlan Elias 	}
906019c4fbaSIlan Elias 
90790099433SEric Lapuyade 	if (!nci_target) {
908019c4fbaSIlan Elias 		pr_err("unable to find the selected target\n");
909019c4fbaSIlan Elias 		return -EINVAL;
910019c4fbaSIlan Elias 	}
911019c4fbaSIlan Elias 
912354a6e70SJeremy Cline 	if (protocol >= NFC_PROTO_MAX) {
913354a6e70SJeremy Cline 		pr_err("the requested nfc protocol is invalid\n");
914354a6e70SJeremy Cline 		return -EINVAL;
915354a6e70SJeremy Cline 	}
916354a6e70SJeremy Cline 
91790099433SEric Lapuyade 	if (!(nci_target->supported_protocols & (1 << protocol))) {
918ed1e0ad8SJoe Perches 		pr_err("target does not support the requested protocol 0x%x\n",
9196a2968aaSIlan Elias 		       protocol);
9206a2968aaSIlan Elias 		return -EINVAL;
9216a2968aaSIlan Elias 	}
9226a2968aaSIlan Elias 
923019c4fbaSIlan Elias 	if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
92490099433SEric Lapuyade 		param.rf_discovery_id = nci_target->logical_idx;
9256a2968aaSIlan Elias 
926019c4fbaSIlan Elias 		if (protocol == NFC_PROTO_JEWEL)
927019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T1T;
928019c4fbaSIlan Elias 		else if (protocol == NFC_PROTO_MIFARE)
929019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T2T;
930019c4fbaSIlan Elias 		else if (protocol == NFC_PROTO_FELICA)
931019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_T3T;
93201d719a2SSamuel Ortiz 		else if (protocol == NFC_PROTO_ISO14443 ||
93301d719a2SSamuel Ortiz 			 protocol == NFC_PROTO_ISO14443_B)
934019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
935019c4fbaSIlan Elias 		else
936019c4fbaSIlan Elias 			param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
937019c4fbaSIlan Elias 
93835d7a6f1SKrzysztof Kozlowski 		rc = nci_request(ndev, nci_rf_discover_select_req, &param,
939019c4fbaSIlan Elias 				 msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT));
940019c4fbaSIlan Elias 	}
941019c4fbaSIlan Elias 
942019c4fbaSIlan Elias 	if (!rc)
943019c4fbaSIlan Elias 		ndev->target_active_prot = protocol;
944019c4fbaSIlan Elias 
945019c4fbaSIlan Elias 	return rc;
9466a2968aaSIlan Elias }
9476a2968aaSIlan Elias 
nci_deactivate_target(struct nfc_dev * nfc_dev,struct nfc_target * target,__u8 mode)94890099433SEric Lapuyade static void nci_deactivate_target(struct nfc_dev *nfc_dev,
94996d4581fSChristophe Ricard 				  struct nfc_target *target,
95096d4581fSChristophe Ricard 				  __u8 mode)
9516a2968aaSIlan Elias {
9526a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
95335d7a6f1SKrzysztof Kozlowski 	unsigned long nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE;
9546a2968aaSIlan Elias 
9556a2968aaSIlan Elias 	if (!ndev->target_active_prot) {
956ed1e0ad8SJoe Perches 		pr_err("unable to deactivate target, no active target\n");
9576a2968aaSIlan Elias 		return;
9586a2968aaSIlan Elias 	}
9596a2968aaSIlan Elias 
9606a2968aaSIlan Elias 	ndev->target_active_prot = 0;
9616a2968aaSIlan Elias 
96296d4581fSChristophe Ricard 	switch (mode) {
96396d4581fSChristophe Ricard 	case NFC_TARGET_MODE_SLEEP:
96496d4581fSChristophe Ricard 		nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE;
96596d4581fSChristophe Ricard 		break;
96696d4581fSChristophe Ricard 	}
96796d4581fSChristophe Ricard 
9688939e47fSIlan Elias 	if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
96935d7a6f1SKrzysztof Kozlowski 		nci_request(ndev, nci_rf_deactivate_req, (void *)nci_mode,
9706a2968aaSIlan Elias 			    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
9716a2968aaSIlan Elias 	}
9726a2968aaSIlan Elias }
9736a2968aaSIlan Elias 
nci_dep_link_up(struct nfc_dev * nfc_dev,struct nfc_target * target,__u8 comm_mode,__u8 * gb,size_t gb_len)974767f19aeSIlan Elias static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
975767f19aeSIlan Elias 			   __u8 comm_mode, __u8 *gb, size_t gb_len)
976767f19aeSIlan Elias {
977767f19aeSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
978767f19aeSIlan Elias 	int rc;
979767f19aeSIlan Elias 
980767f19aeSIlan Elias 	pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
981767f19aeSIlan Elias 
982767f19aeSIlan Elias 	rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
983767f19aeSIlan Elias 	if (rc)
984767f19aeSIlan Elias 		return rc;
985767f19aeSIlan Elias 
986767f19aeSIlan Elias 	rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
987767f19aeSIlan Elias 					  ndev->remote_gb_len);
988767f19aeSIlan Elias 	if (!rc)
989767f19aeSIlan Elias 		rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
990767f19aeSIlan Elias 					NFC_RF_INITIATOR);
991767f19aeSIlan Elias 
992767f19aeSIlan Elias 	return rc;
993767f19aeSIlan Elias }
994767f19aeSIlan Elias 
nci_dep_link_down(struct nfc_dev * nfc_dev)995767f19aeSIlan Elias static int nci_dep_link_down(struct nfc_dev *nfc_dev)
996767f19aeSIlan Elias {
997d7979e13SJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
998d7979e13SJulien Lefrique 	int rc;
999d7979e13SJulien Lefrique 
1000d7979e13SJulien Lefrique 	if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
100196d4581fSChristophe Ricard 		nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE);
1002d7979e13SJulien Lefrique 	} else {
1003d7979e13SJulien Lefrique 		if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
1004d7979e13SJulien Lefrique 		    atomic_read(&ndev->state) == NCI_DISCOVERY) {
100535d7a6f1SKrzysztof Kozlowski 			nci_request(ndev, nci_rf_deactivate_req, (void *)0,
1006d7979e13SJulien Lefrique 				    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
1007d7979e13SJulien Lefrique 		}
1008d7979e13SJulien Lefrique 
1009d7979e13SJulien Lefrique 		rc = nfc_tm_deactivated(nfc_dev);
1010d7979e13SJulien Lefrique 		if (rc)
1011d7979e13SJulien Lefrique 			pr_err("error when signaling tm deactivation\n");
1012d7979e13SJulien Lefrique 	}
1013767f19aeSIlan Elias 
1014767f19aeSIlan Elias 	return 0;
1015767f19aeSIlan Elias }
1016767f19aeSIlan Elias 
1017767f19aeSIlan Elias 
nci_transceive(struct nfc_dev * nfc_dev,struct nfc_target * target,struct sk_buff * skb,data_exchange_cb_t cb,void * cb_context)1018be9ae4ceSSamuel Ortiz static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
10196a2968aaSIlan Elias 			  struct sk_buff *skb,
1020eb9bc6e9SSamuel Ortiz 			  data_exchange_cb_t cb, void *cb_context)
10216a2968aaSIlan Elias {
10226a2968aaSIlan Elias 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
102338f04c6bSIlan Elias 	int rc;
10244aeee687SChristophe Ricard 	struct nci_conn_info *conn_info;
10254aeee687SChristophe Ricard 
102612bdf27dSChristophe Ricard 	conn_info = ndev->rf_conn_info;
10274aeee687SChristophe Ricard 	if (!conn_info)
10284aeee687SChristophe Ricard 		return -EPROTO;
10296a2968aaSIlan Elias 
103090099433SEric Lapuyade 	pr_debug("target_idx %d, len %d\n", target->idx, skb->len);
10316a2968aaSIlan Elias 
10326a2968aaSIlan Elias 	if (!ndev->target_active_prot) {
1033ed1e0ad8SJoe Perches 		pr_err("unable to exchange data, no active target\n");
10346a2968aaSIlan Elias 		return -EINVAL;
10356a2968aaSIlan Elias 	}
10366a2968aaSIlan Elias 
103738f04c6bSIlan Elias 	if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags))
103838f04c6bSIlan Elias 		return -EBUSY;
103938f04c6bSIlan Elias 
10406a2968aaSIlan Elias 	/* store cb and context to be used on receiving data */
10414aeee687SChristophe Ricard 	conn_info->data_exchange_cb = cb;
10424aeee687SChristophe Ricard 	conn_info->data_exchange_cb_context = cb_context;
10436a2968aaSIlan Elias 
1044e8c0dacdSIlan Elias 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
104538f04c6bSIlan Elias 	if (rc)
104638f04c6bSIlan Elias 		clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
104738f04c6bSIlan Elias 
104838f04c6bSIlan Elias 	return rc;
10496a2968aaSIlan Elias }
10506a2968aaSIlan Elias 
nci_tm_send(struct nfc_dev * nfc_dev,struct sk_buff * skb)1051485f442fSJulien Lefrique static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
1052485f442fSJulien Lefrique {
1053485f442fSJulien Lefrique 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1054485f442fSJulien Lefrique 	int rc;
1055485f442fSJulien Lefrique 
1056485f442fSJulien Lefrique 	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
1057485f442fSJulien Lefrique 	if (rc)
1058485f442fSJulien Lefrique 		pr_err("unable to send data\n");
1059485f442fSJulien Lefrique 
1060485f442fSJulien Lefrique 	return rc;
1061485f442fSJulien Lefrique }
1062485f442fSJulien Lefrique 
nci_enable_se(struct nfc_dev * nfc_dev,u32 se_idx)10630a946301SSamuel Ortiz static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
10640a946301SSamuel Ortiz {
106593bca2bfSChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
106693bca2bfSChristophe Ricard 
106793bca2bfSChristophe Ricard 	if (ndev->ops->enable_se)
106893bca2bfSChristophe Ricard 		return ndev->ops->enable_se(ndev, se_idx);
106993bca2bfSChristophe Ricard 
10700a946301SSamuel Ortiz 	return 0;
10710a946301SSamuel Ortiz }
10720a946301SSamuel Ortiz 
nci_disable_se(struct nfc_dev * nfc_dev,u32 se_idx)10730a946301SSamuel Ortiz static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
10740a946301SSamuel Ortiz {
1075e9ef9431SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1076e9ef9431SChristophe Ricard 
1077e9ef9431SChristophe Ricard 	if (ndev->ops->disable_se)
1078e9ef9431SChristophe Ricard 		return ndev->ops->disable_se(ndev, se_idx);
1079e9ef9431SChristophe Ricard 
10800a946301SSamuel Ortiz 	return 0;
10810a946301SSamuel Ortiz }
10820a946301SSamuel Ortiz 
nci_discover_se(struct nfc_dev * nfc_dev)10830a946301SSamuel Ortiz static int nci_discover_se(struct nfc_dev *nfc_dev)
10840a946301SSamuel Ortiz {
1085fa00e8feSChristophe Ricard 	int r;
1086ba4db551SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1087ba4db551SChristophe Ricard 
1088fa00e8feSChristophe Ricard 	if (ndev->ops->discover_se) {
1089fa00e8feSChristophe Ricard 		r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE);
1090fa00e8feSChristophe Ricard 		if (r != NCI_STATUS_OK)
1091fa00e8feSChristophe Ricard 			return -EPROTO;
1092fa00e8feSChristophe Ricard 
1093ba4db551SChristophe Ricard 		return ndev->ops->discover_se(ndev);
1094fa00e8feSChristophe Ricard 	}
1095ba4db551SChristophe Ricard 
10960a946301SSamuel Ortiz 	return 0;
10970a946301SSamuel Ortiz }
10980a946301SSamuel Ortiz 
nci_se_io(struct nfc_dev * nfc_dev,u32 se_idx,u8 * apdu,size_t apdu_length,se_io_cb_t cb,void * cb_context)1099a688bf55SChristophe Ricard static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
1100a688bf55SChristophe Ricard 		     u8 *apdu, size_t apdu_length,
1101a688bf55SChristophe Ricard 		     se_io_cb_t cb, void *cb_context)
1102a688bf55SChristophe Ricard {
1103a688bf55SChristophe Ricard 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
1104a688bf55SChristophe Ricard 
1105a688bf55SChristophe Ricard 	if (ndev->ops->se_io)
1106a688bf55SChristophe Ricard 		return ndev->ops->se_io(ndev, se_idx, apdu,
1107a688bf55SChristophe Ricard 				apdu_length, cb, cb_context);
1108a688bf55SChristophe Ricard 
1109a688bf55SChristophe Ricard 	return 0;
1110a688bf55SChristophe Ricard }
1111a688bf55SChristophe Ricard 
nci_fw_download(struct nfc_dev * nfc_dev,const char * firmware_name)111225af01edSClément Perrochaud static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
111325af01edSClément Perrochaud {
111425af01edSClément Perrochaud 	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
111525af01edSClément Perrochaud 
111625af01edSClément Perrochaud 	if (!ndev->ops->fw_download)
111725af01edSClément Perrochaud 		return -ENOTSUPP;
111825af01edSClément Perrochaud 
111925af01edSClément Perrochaud 	return ndev->ops->fw_download(ndev, firmware_name);
112025af01edSClément Perrochaud }
112125af01edSClément Perrochaud 
1122f6c802a7SKrzysztof Kozlowski static const struct nfc_ops nci_nfc_ops = {
11236a2968aaSIlan Elias 	.dev_up = nci_dev_up,
11246a2968aaSIlan Elias 	.dev_down = nci_dev_down,
11256a2968aaSIlan Elias 	.start_poll = nci_start_poll,
11266a2968aaSIlan Elias 	.stop_poll = nci_stop_poll,
1127767f19aeSIlan Elias 	.dep_link_up = nci_dep_link_up,
1128767f19aeSIlan Elias 	.dep_link_down = nci_dep_link_down,
11296a2968aaSIlan Elias 	.activate_target = nci_activate_target,
11306a2968aaSIlan Elias 	.deactivate_target = nci_deactivate_target,
1131be9ae4ceSSamuel Ortiz 	.im_transceive = nci_transceive,
1132485f442fSJulien Lefrique 	.tm_send = nci_tm_send,
11330a946301SSamuel Ortiz 	.enable_se = nci_enable_se,
11340a946301SSamuel Ortiz 	.disable_se = nci_disable_se,
11350a946301SSamuel Ortiz 	.discover_se = nci_discover_se,
1136a688bf55SChristophe Ricard 	.se_io = nci_se_io,
113725af01edSClément Perrochaud 	.fw_download = nci_fw_download,
11386a2968aaSIlan Elias };
11396a2968aaSIlan Elias 
11406a2968aaSIlan Elias /* ---- Interface to NCI drivers ---- */
11416a2968aaSIlan Elias /**
11426a2968aaSIlan Elias  * nci_allocate_device - allocate a new nci device
11436a2968aaSIlan Elias  *
11446a2968aaSIlan Elias  * @ops: device operations
11456a2968aaSIlan Elias  * @supported_protocols: NFC protocols supported by the device
11467cdda1c1SAndrew Lunn  * @tx_headroom: Reserved space at beginning of skb
11477cdda1c1SAndrew Lunn  * @tx_tailroom: Reserved space at end of skb
11486a2968aaSIlan Elias  */
nci_allocate_device(const struct nci_ops * ops,__u32 supported_protocols,int tx_headroom,int tx_tailroom)1149b9c28286SKrzysztof Kozlowski struct nci_dev *nci_allocate_device(const struct nci_ops *ops,
11506a2968aaSIlan Elias 				    __u32 supported_protocols,
1151eb9bc6e9SSamuel Ortiz 				    int tx_headroom, int tx_tailroom)
11526a2968aaSIlan Elias {
11538ebafde0SDan Carpenter 	struct nci_dev *ndev;
11546a2968aaSIlan Elias 
115524bf3304SJoe Perches 	pr_debug("supported_protocols 0x%x\n", supported_protocols);
11566a2968aaSIlan Elias 
11576a2968aaSIlan Elias 	if (!ops->open || !ops->close || !ops->send)
11588ebafde0SDan Carpenter 		return NULL;
11596a2968aaSIlan Elias 
11606a2968aaSIlan Elias 	if (!supported_protocols)
11618ebafde0SDan Carpenter 		return NULL;
11626a2968aaSIlan Elias 
11636a2968aaSIlan Elias 	ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL);
11646a2968aaSIlan Elias 	if (!ndev)
11658ebafde0SDan Carpenter 		return NULL;
11666a2968aaSIlan Elias 
11676a2968aaSIlan Elias 	ndev->ops = ops;
1168b6355e97SSamuel Ortiz 
1169b6355e97SSamuel Ortiz 	if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) {
1170b6355e97SSamuel Ortiz 		pr_err("Too many proprietary commands: %zd\n",
1171b6355e97SSamuel Ortiz 		       ops->n_prop_ops);
1172b9c28286SKrzysztof Kozlowski 		goto free_nci;
1173b6355e97SSamuel Ortiz 	}
1174b6355e97SSamuel Ortiz 
11756a2968aaSIlan Elias 	ndev->tx_headroom = tx_headroom;
11766a2968aaSIlan Elias 	ndev->tx_tailroom = tx_tailroom;
11779bec44bfSAxel Lin 	init_completion(&ndev->req_completion);
11786a2968aaSIlan Elias 
11796a2968aaSIlan Elias 	ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
11806a2968aaSIlan Elias 					    supported_protocols,
11816a2968aaSIlan Elias 					    tx_headroom + NCI_DATA_HDR_SIZE,
11826a2968aaSIlan Elias 					    tx_tailroom);
11836a2968aaSIlan Elias 	if (!ndev->nfc_dev)
118411f54f22SChristophe Ricard 		goto free_nci;
118511f54f22SChristophe Ricard 
118611f54f22SChristophe Ricard 	ndev->hci_dev = nci_hci_allocate(ndev);
118711f54f22SChristophe Ricard 	if (!ndev->hci_dev)
118811f54f22SChristophe Ricard 		goto free_nfc;
11896a2968aaSIlan Elias 
11906a2968aaSIlan Elias 	nfc_set_drvdata(ndev->nfc_dev, ndev);
11916a2968aaSIlan Elias 
11928ebafde0SDan Carpenter 	return ndev;
11936a2968aaSIlan Elias 
119411f54f22SChristophe Ricard free_nfc:
119520777bc5SJohan Hovold 	nfc_free_device(ndev->nfc_dev);
119611f54f22SChristophe Ricard free_nci:
11976a2968aaSIlan Elias 	kfree(ndev);
11988ebafde0SDan Carpenter 	return NULL;
11996a2968aaSIlan Elias }
12006a2968aaSIlan Elias EXPORT_SYMBOL(nci_allocate_device);
12016a2968aaSIlan Elias 
12026a2968aaSIlan Elias /**
12036a2968aaSIlan Elias  * nci_free_device - deallocate nci device
12046a2968aaSIlan Elias  *
12056a2968aaSIlan Elias  * @ndev: The nci device to deallocate
12066a2968aaSIlan Elias  */
nci_free_device(struct nci_dev * ndev)12076a2968aaSIlan Elias void nci_free_device(struct nci_dev *ndev)
12086a2968aaSIlan Elias {
12096a2968aaSIlan Elias 	nfc_free_device(ndev->nfc_dev);
1210e0652f8bSDongliang Mu 	nci_hci_deallocate(ndev);
121116d3f507SFedor Pchelkin 
121216d3f507SFedor Pchelkin 	/* drop partial rx data packet if present */
121316d3f507SFedor Pchelkin 	if (ndev->rx_data_reassembly)
121416d3f507SFedor Pchelkin 		kfree_skb(ndev->rx_data_reassembly);
12156a2968aaSIlan Elias 	kfree(ndev);
12166a2968aaSIlan Elias }
12176a2968aaSIlan Elias EXPORT_SYMBOL(nci_free_device);
12186a2968aaSIlan Elias 
12196a2968aaSIlan Elias /**
12206a2968aaSIlan Elias  * nci_register_device - register a nci device in the nfc subsystem
12216a2968aaSIlan Elias  *
1222ffbab1c9SAndrew Lunn  * @ndev: The nci device to register
12236a2968aaSIlan Elias  */
nci_register_device(struct nci_dev * ndev)12246a2968aaSIlan Elias int nci_register_device(struct nci_dev *ndev)
12256a2968aaSIlan Elias {
12266a2968aaSIlan Elias 	int rc;
12276a2968aaSIlan Elias 	struct device *dev = &ndev->nfc_dev->dev;
12286a2968aaSIlan Elias 	char name[32];
12296a2968aaSIlan Elias 
12306a2968aaSIlan Elias 	ndev->flags = 0;
12316a2968aaSIlan Elias 
12326a2968aaSIlan Elias 	INIT_WORK(&ndev->cmd_work, nci_cmd_work);
12336a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev));
12346a2968aaSIlan Elias 	ndev->cmd_wq = create_singlethread_workqueue(name);
12356a2968aaSIlan Elias 	if (!ndev->cmd_wq) {
12366a2968aaSIlan Elias 		rc = -ENOMEM;
12373c1c0f5dSVincent Cuissard 		goto exit;
12386a2968aaSIlan Elias 	}
12396a2968aaSIlan Elias 
12406a2968aaSIlan Elias 	INIT_WORK(&ndev->rx_work, nci_rx_work);
12416a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev));
12426a2968aaSIlan Elias 	ndev->rx_wq = create_singlethread_workqueue(name);
12436a2968aaSIlan Elias 	if (!ndev->rx_wq) {
12446a2968aaSIlan Elias 		rc = -ENOMEM;
12456a2968aaSIlan Elias 		goto destroy_cmd_wq_exit;
12466a2968aaSIlan Elias 	}
12476a2968aaSIlan Elias 
12486a2968aaSIlan Elias 	INIT_WORK(&ndev->tx_work, nci_tx_work);
12496a2968aaSIlan Elias 	snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev));
12506a2968aaSIlan Elias 	ndev->tx_wq = create_singlethread_workqueue(name);
12516a2968aaSIlan Elias 	if (!ndev->tx_wq) {
12526a2968aaSIlan Elias 		rc = -ENOMEM;
12536a2968aaSIlan Elias 		goto destroy_rx_wq_exit;
12546a2968aaSIlan Elias 	}
12556a2968aaSIlan Elias 
12566a2968aaSIlan Elias 	skb_queue_head_init(&ndev->cmd_q);
12576a2968aaSIlan Elias 	skb_queue_head_init(&ndev->rx_q);
12586a2968aaSIlan Elias 	skb_queue_head_init(&ndev->tx_q);
12596a2968aaSIlan Elias 
1260e99e88a9SKees Cook 	timer_setup(&ndev->cmd_timer, nci_cmd_timer, 0);
1261e99e88a9SKees Cook 	timer_setup(&ndev->data_timer, nci_data_timer, 0);
12626a2968aaSIlan Elias 
12636a2968aaSIlan Elias 	mutex_init(&ndev->req_lock);
12644aeee687SChristophe Ricard 	INIT_LIST_HEAD(&ndev->conn_info_list);
12656a2968aaSIlan Elias 
12663c1c0f5dSVincent Cuissard 	rc = nfc_register_device(ndev->nfc_dev);
12673c1c0f5dSVincent Cuissard 	if (rc)
12680b4a66a3SWang Hai 		goto destroy_tx_wq_exit;
12693c1c0f5dSVincent Cuissard 
12706a2968aaSIlan Elias 	goto exit;
12716a2968aaSIlan Elias 
12720b4a66a3SWang Hai destroy_tx_wq_exit:
12730b4a66a3SWang Hai 	destroy_workqueue(ndev->tx_wq);
12740b4a66a3SWang Hai 
12756a2968aaSIlan Elias destroy_rx_wq_exit:
12766a2968aaSIlan Elias 	destroy_workqueue(ndev->rx_wq);
12776a2968aaSIlan Elias 
12786a2968aaSIlan Elias destroy_cmd_wq_exit:
12796a2968aaSIlan Elias 	destroy_workqueue(ndev->cmd_wq);
12806a2968aaSIlan Elias 
12816a2968aaSIlan Elias exit:
12826a2968aaSIlan Elias 	return rc;
12836a2968aaSIlan Elias }
12846a2968aaSIlan Elias EXPORT_SYMBOL(nci_register_device);
12856a2968aaSIlan Elias 
12866a2968aaSIlan Elias /**
12876a2968aaSIlan Elias  * nci_unregister_device - unregister a nci device in the nfc subsystem
12886a2968aaSIlan Elias  *
1289ffbab1c9SAndrew Lunn  * @ndev: The nci device to unregister
12906a2968aaSIlan Elias  */
nci_unregister_device(struct nci_dev * ndev)12916a2968aaSIlan Elias void nci_unregister_device(struct nci_dev *ndev)
12926a2968aaSIlan Elias {
12934aeee687SChristophe Ricard 	struct nci_conn_info *conn_info, *n;
12944aeee687SChristophe Ricard 
129548b71a9eSLin Ma 	/* This set_bit is not protected with specialized barrier,
129648b71a9eSLin Ma 	 * However, it is fine because the mutex_lock(&ndev->req_lock);
129748b71a9eSLin Ma 	 * in nci_close_device() will help to emit one.
129848b71a9eSLin Ma 	 */
129948b71a9eSLin Ma 	set_bit(NCI_UNREG, &ndev->flags);
130048b71a9eSLin Ma 
13016a2968aaSIlan Elias 	nci_close_device(ndev);
13026a2968aaSIlan Elias 
13036a2968aaSIlan Elias 	destroy_workqueue(ndev->cmd_wq);
13046a2968aaSIlan Elias 	destroy_workqueue(ndev->rx_wq);
13056a2968aaSIlan Elias 	destroy_workqueue(ndev->tx_wq);
13066a2968aaSIlan Elias 
13074aeee687SChristophe Ricard 	list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) {
13084aeee687SChristophe Ricard 		list_del(&conn_info->list);
13094aeee687SChristophe Ricard 		/* conn_info is allocated with devm_kzalloc */
13104aeee687SChristophe Ricard 	}
13114aeee687SChristophe Ricard 
13126a2968aaSIlan Elias 	nfc_unregister_device(ndev->nfc_dev);
13136a2968aaSIlan Elias }
13146a2968aaSIlan Elias EXPORT_SYMBOL(nci_unregister_device);
13156a2968aaSIlan Elias 
13166a2968aaSIlan Elias /**
13176a2968aaSIlan Elias  * nci_recv_frame - receive frame from NCI drivers
13186a2968aaSIlan Elias  *
13191095e69fSFrederic Danis  * @ndev: The nci device
13206a2968aaSIlan Elias  * @skb: The sk_buff to receive
13216a2968aaSIlan Elias  */
nci_recv_frame(struct nci_dev * ndev,struct sk_buff * skb)13221095e69fSFrederic Danis int nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
13236a2968aaSIlan Elias {
132424bf3304SJoe Perches 	pr_debug("len %d\n", skb->len);
13256a2968aaSIlan Elias 
1326874934f4SSzymon Janc 	if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
1327874934f4SSzymon Janc 	    !test_bit(NCI_INIT, &ndev->flags))) {
13286a2968aaSIlan Elias 		kfree_skb(skb);
13296a2968aaSIlan Elias 		return -ENXIO;
13306a2968aaSIlan Elias 	}
13316a2968aaSIlan Elias 
13326a2968aaSIlan Elias 	/* Queue frame for rx worker thread */
13336a2968aaSIlan Elias 	skb_queue_tail(&ndev->rx_q, skb);
13346a2968aaSIlan Elias 	queue_work(ndev->rx_wq, &ndev->rx_work);
13356a2968aaSIlan Elias 
13366a2968aaSIlan Elias 	return 0;
13376a2968aaSIlan Elias }
13386a2968aaSIlan Elias EXPORT_SYMBOL(nci_recv_frame);
13396a2968aaSIlan Elias 
nci_send_frame(struct nci_dev * ndev,struct sk_buff * skb)1340e5629d29SVincent Cuissard int nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
13416a2968aaSIlan Elias {
134224bf3304SJoe Perches 	pr_debug("len %d\n", skb->len);
13436a2968aaSIlan Elias 
13446a2968aaSIlan Elias 	if (!ndev) {
13456a2968aaSIlan Elias 		kfree_skb(skb);
13466a2968aaSIlan Elias 		return -ENODEV;
13476a2968aaSIlan Elias 	}
13486a2968aaSIlan Elias 
13496a2968aaSIlan Elias 	/* Get rid of skb owner, prior to sending to the driver. */
13506a2968aaSIlan Elias 	skb_orphan(skb);
13516a2968aaSIlan Elias 
135205158296SHiren Tandel 	/* Send copy to sniffer */
135305158296SHiren Tandel 	nfc_send_to_raw_sock(ndev->nfc_dev, skb,
135405158296SHiren Tandel 			     RAW_PAYLOAD_NCI, NFC_DIRECTION_TX);
135505158296SHiren Tandel 
13561095e69fSFrederic Danis 	return ndev->ops->send(ndev, skb);
13576a2968aaSIlan Elias }
1358e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_frame);
13596a2968aaSIlan Elias 
13606a2968aaSIlan Elias /* Send NCI command */
nci_send_cmd(struct nci_dev * ndev,__u16 opcode,__u8 plen,const void * payload)136148d54403SKrzysztof Kozlowski int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, const void *payload)
13626a2968aaSIlan Elias {
13636a2968aaSIlan Elias 	struct nci_ctrl_hdr *hdr;
13646a2968aaSIlan Elias 	struct sk_buff *skb;
13656a2968aaSIlan Elias 
136624bf3304SJoe Perches 	pr_debug("opcode 0x%x, plen %d\n", opcode, plen);
13676a2968aaSIlan Elias 
13686a2968aaSIlan Elias 	skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL);
13696a2968aaSIlan Elias 	if (!skb) {
1370ed1e0ad8SJoe Perches 		pr_err("no memory for command\n");
13716a2968aaSIlan Elias 		return -ENOMEM;
13726a2968aaSIlan Elias 	}
13736a2968aaSIlan Elias 
13744df864c1SJohannes Berg 	hdr = skb_put(skb, NCI_CTRL_HDR_SIZE);
13756a2968aaSIlan Elias 	hdr->gid = nci_opcode_gid(opcode);
13766a2968aaSIlan Elias 	hdr->oid = nci_opcode_oid(opcode);
13776a2968aaSIlan Elias 	hdr->plen = plen;
13786a2968aaSIlan Elias 
13796a2968aaSIlan Elias 	nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT);
13806a2968aaSIlan Elias 	nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
13816a2968aaSIlan Elias 
13826a2968aaSIlan Elias 	if (plen)
138359ae1d12SJohannes Berg 		skb_put_data(skb, payload, plen);
13846a2968aaSIlan Elias 
13856a2968aaSIlan Elias 	skb_queue_tail(&ndev->cmd_q, skb);
13866a2968aaSIlan Elias 	queue_work(ndev->cmd_wq, &ndev->cmd_work);
13876a2968aaSIlan Elias 
13886a2968aaSIlan Elias 	return 0;
13896a2968aaSIlan Elias }
1390e5629d29SVincent Cuissard EXPORT_SYMBOL(nci_send_cmd);
13916a2968aaSIlan Elias 
1392b6355e97SSamuel Ortiz /* Proprietary commands API */
ops_cmd_lookup(const struct nci_driver_ops * ops,size_t n_ops,__u16 opcode)1393cb8caa3cSKrzysztof Kozlowski static const struct nci_driver_ops *ops_cmd_lookup(const struct nci_driver_ops *ops,
13940a97a3cbSRobert Dolca 						   size_t n_ops,
1395b6355e97SSamuel Ortiz 						   __u16 opcode)
1396b6355e97SSamuel Ortiz {
1397b6355e97SSamuel Ortiz 	size_t i;
1398cb8caa3cSKrzysztof Kozlowski 	const struct nci_driver_ops *op;
1399b6355e97SSamuel Ortiz 
14000a97a3cbSRobert Dolca 	if (!ops || !n_ops)
1401b6355e97SSamuel Ortiz 		return NULL;
1402b6355e97SSamuel Ortiz 
14030a97a3cbSRobert Dolca 	for (i = 0; i < n_ops; i++) {
14040a97a3cbSRobert Dolca 		op = &ops[i];
14050a97a3cbSRobert Dolca 		if (op->opcode == opcode)
14060a97a3cbSRobert Dolca 			return op;
1407b6355e97SSamuel Ortiz 	}
1408b6355e97SSamuel Ortiz 
1409b6355e97SSamuel Ortiz 	return NULL;
1410b6355e97SSamuel Ortiz }
1411b6355e97SSamuel Ortiz 
nci_op_rsp_packet(struct nci_dev * ndev,__u16 rsp_opcode,struct sk_buff * skb,const struct nci_driver_ops * ops,size_t n_ops)14120a97a3cbSRobert Dolca static int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
1413cb8caa3cSKrzysztof Kozlowski 			     struct sk_buff *skb, const struct nci_driver_ops *ops,
14140a97a3cbSRobert Dolca 			     size_t n_ops)
1415b6355e97SSamuel Ortiz {
1416cb8caa3cSKrzysztof Kozlowski 	const struct nci_driver_ops *op;
1417b6355e97SSamuel Ortiz 
14180a97a3cbSRobert Dolca 	op = ops_cmd_lookup(ops, n_ops, rsp_opcode);
14190a97a3cbSRobert Dolca 	if (!op || !op->rsp)
1420b6355e97SSamuel Ortiz 		return -ENOTSUPP;
1421b6355e97SSamuel Ortiz 
14220a97a3cbSRobert Dolca 	return op->rsp(ndev, skb);
1423b6355e97SSamuel Ortiz }
1424b6355e97SSamuel Ortiz 
nci_op_ntf_packet(struct nci_dev * ndev,__u16 ntf_opcode,struct sk_buff * skb,const struct nci_driver_ops * ops,size_t n_ops)14250a97a3cbSRobert Dolca static int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
1426cb8caa3cSKrzysztof Kozlowski 			     struct sk_buff *skb, const struct nci_driver_ops *ops,
14270a97a3cbSRobert Dolca 			     size_t n_ops)
1428b6355e97SSamuel Ortiz {
1429cb8caa3cSKrzysztof Kozlowski 	const struct nci_driver_ops *op;
1430b6355e97SSamuel Ortiz 
14310a97a3cbSRobert Dolca 	op = ops_cmd_lookup(ops, n_ops, ntf_opcode);
14320a97a3cbSRobert Dolca 	if (!op || !op->ntf)
1433b6355e97SSamuel Ortiz 		return -ENOTSUPP;
1434b6355e97SSamuel Ortiz 
14350a97a3cbSRobert Dolca 	return op->ntf(ndev, skb);
14360a97a3cbSRobert Dolca }
14370a97a3cbSRobert Dolca 
nci_prop_rsp_packet(struct nci_dev * ndev,__u16 opcode,struct sk_buff * skb)1438f1163174SRobert Dolca int nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
14390a97a3cbSRobert Dolca 			struct sk_buff *skb)
14400a97a3cbSRobert Dolca {
14410a97a3cbSRobert Dolca 	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops,
14420a97a3cbSRobert Dolca 				 ndev->ops->n_prop_ops);
14430a97a3cbSRobert Dolca }
14440a97a3cbSRobert Dolca 
nci_prop_ntf_packet(struct nci_dev * ndev,__u16 opcode,struct sk_buff * skb)1445f1163174SRobert Dolca int nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
14460a97a3cbSRobert Dolca 			struct sk_buff *skb)
14470a97a3cbSRobert Dolca {
14480a97a3cbSRobert Dolca 	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops,
14490a97a3cbSRobert Dolca 				 ndev->ops->n_prop_ops);
14500a97a3cbSRobert Dolca }
14510a97a3cbSRobert Dolca 
nci_core_rsp_packet(struct nci_dev * ndev,__u16 opcode,struct sk_buff * skb)1452f1163174SRobert Dolca int nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode,
14530a97a3cbSRobert Dolca 			struct sk_buff *skb)
14540a97a3cbSRobert Dolca {
14550a97a3cbSRobert Dolca 	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops,
14560a97a3cbSRobert Dolca 				  ndev->ops->n_core_ops);
14570a97a3cbSRobert Dolca }
14580a97a3cbSRobert Dolca 
nci_core_ntf_packet(struct nci_dev * ndev,__u16 opcode,struct sk_buff * skb)1459f1163174SRobert Dolca int nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode,
14600a97a3cbSRobert Dolca 			struct sk_buff *skb)
14610a97a3cbSRobert Dolca {
14620a97a3cbSRobert Dolca 	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops,
14630a97a3cbSRobert Dolca 				 ndev->ops->n_core_ops);
1464b6355e97SSamuel Ortiz }
1465b6355e97SSamuel Ortiz 
nci_valid_size(struct sk_buff * skb)1466e53a7f8aSRyosuke Yasuoka static bool nci_valid_size(struct sk_buff *skb)
1467e53a7f8aSRyosuke Yasuoka {
1468e53a7f8aSRyosuke Yasuoka 	BUILD_BUG_ON(NCI_CTRL_HDR_SIZE != NCI_DATA_HDR_SIZE);
1469e53a7f8aSRyosuke Yasuoka 	unsigned int hdr_size = NCI_CTRL_HDR_SIZE;
1470e53a7f8aSRyosuke Yasuoka 
1471e53a7f8aSRyosuke Yasuoka 	if (skb->len < hdr_size ||
1472e53a7f8aSRyosuke Yasuoka 	    !nci_plen(skb->data) ||
1473e53a7f8aSRyosuke Yasuoka 	    skb->len < hdr_size + nci_plen(skb->data)) {
1474e53a7f8aSRyosuke Yasuoka 		return false;
1475e53a7f8aSRyosuke Yasuoka 	}
1476e53a7f8aSRyosuke Yasuoka 	return true;
1477e53a7f8aSRyosuke Yasuoka }
1478e53a7f8aSRyosuke Yasuoka 
14796a2968aaSIlan Elias /* ---- NCI TX Data worker thread ---- */
14806a2968aaSIlan Elias 
nci_tx_work(struct work_struct * work)14816a2968aaSIlan Elias static void nci_tx_work(struct work_struct *work)
14826a2968aaSIlan Elias {
14836a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work);
14844aeee687SChristophe Ricard 	struct nci_conn_info *conn_info;
14856a2968aaSIlan Elias 	struct sk_buff *skb;
14866a2968aaSIlan Elias 
14874aeee687SChristophe Ricard 	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
14884aeee687SChristophe Ricard 	if (!conn_info)
14894aeee687SChristophe Ricard 		return;
14904aeee687SChristophe Ricard 
14914aeee687SChristophe Ricard 	pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt));
14926a2968aaSIlan Elias 
14936a2968aaSIlan Elias 	/* Send queued tx data */
14944aeee687SChristophe Ricard 	while (atomic_read(&conn_info->credits_cnt)) {
14956a2968aaSIlan Elias 		skb = skb_dequeue(&ndev->tx_q);
14966a2968aaSIlan Elias 		if (!skb)
14976a2968aaSIlan Elias 			return;
14987e8cdc97SDmitry Vyukov 		kcov_remote_start_common(skb_get_kcov_handle(skb));
14996a2968aaSIlan Elias 
1500db98c829SIlan Elias 		/* Check if data flow control is used */
15014aeee687SChristophe Ricard 		if (atomic_read(&conn_info->credits_cnt) !=
1502db98c829SIlan Elias 		    NCI_DATA_FLOW_CONTROL_NOT_USED)
15034aeee687SChristophe Ricard 			atomic_dec(&conn_info->credits_cnt);
15046a2968aaSIlan Elias 
150520c239c1SJoe Perches 		pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
15066a2968aaSIlan Elias 			 nci_pbf(skb->data),
15076a2968aaSIlan Elias 			 nci_conn_id(skb->data),
15086a2968aaSIlan Elias 			 nci_plen(skb->data));
15096a2968aaSIlan Elias 
15101095e69fSFrederic Danis 		nci_send_frame(ndev, skb);
1511c4bf98b2SIlan Elias 
1512c4bf98b2SIlan Elias 		mod_timer(&ndev->data_timer,
1513c4bf98b2SIlan Elias 			  jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
15147e8cdc97SDmitry Vyukov 		kcov_remote_stop();
15156a2968aaSIlan Elias 	}
15166a2968aaSIlan Elias }
15176a2968aaSIlan Elias 
15186a2968aaSIlan Elias /* ----- NCI RX worker thread (data & control) ----- */
15196a2968aaSIlan Elias 
nci_rx_work(struct work_struct * work)15206a2968aaSIlan Elias static void nci_rx_work(struct work_struct *work)
15216a2968aaSIlan Elias {
15226a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work);
15236a2968aaSIlan Elias 	struct sk_buff *skb;
15246a2968aaSIlan Elias 
15257e8cdc97SDmitry Vyukov 	for (; (skb = skb_dequeue(&ndev->rx_q)); kcov_remote_stop()) {
15267e8cdc97SDmitry Vyukov 		kcov_remote_start_common(skb_get_kcov_handle(skb));
152705158296SHiren Tandel 
152805158296SHiren Tandel 		/* Send copy to sniffer */
152905158296SHiren Tandel 		nfc_send_to_raw_sock(ndev->nfc_dev, skb,
153005158296SHiren Tandel 				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
153105158296SHiren Tandel 
1532e53a7f8aSRyosuke Yasuoka 		if (!nci_valid_size(skb)) {
1533a946ebeeSRyosuke Yasuoka 			kfree_skb(skb);
153446e72ebcSRyosuke Yasuoka 			continue;
1535a946ebeeSRyosuke Yasuoka 		}
1536a946ebeeSRyosuke Yasuoka 
15376a2968aaSIlan Elias 		/* Process frame */
15386a2968aaSIlan Elias 		switch (nci_mt(skb->data)) {
15396a2968aaSIlan Elias 		case NCI_MT_RSP_PKT:
15406a2968aaSIlan Elias 			nci_rsp_packet(ndev, skb);
15416a2968aaSIlan Elias 			break;
15426a2968aaSIlan Elias 
15436a2968aaSIlan Elias 		case NCI_MT_NTF_PKT:
15446a2968aaSIlan Elias 			nci_ntf_packet(ndev, skb);
15456a2968aaSIlan Elias 			break;
15466a2968aaSIlan Elias 
15476a2968aaSIlan Elias 		case NCI_MT_DATA_PKT:
15486a2968aaSIlan Elias 			nci_rx_data_packet(ndev, skb);
15496a2968aaSIlan Elias 			break;
15506a2968aaSIlan Elias 
15516a2968aaSIlan Elias 		default:
1552ed1e0ad8SJoe Perches 			pr_err("unknown MT 0x%x\n", nci_mt(skb->data));
15536a2968aaSIlan Elias 			kfree_skb(skb);
15546a2968aaSIlan Elias 			break;
15556a2968aaSIlan Elias 		}
15566a2968aaSIlan Elias 	}
1557c4bf98b2SIlan Elias 
1558d3295869SZheng Yongjun 	/* check if a data exchange timeout has occurred */
1559c4bf98b2SIlan Elias 	if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
1560c4bf98b2SIlan Elias 		/* complete the data exchange transaction, if exists */
1561c4bf98b2SIlan Elias 		if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
15624aeee687SChristophe Ricard 			nci_data_exchange_complete(ndev, NULL,
15634aeee687SChristophe Ricard 						   ndev->cur_conn_id,
15644aeee687SChristophe Ricard 						   -ETIMEDOUT);
1565c4bf98b2SIlan Elias 
1566c4bf98b2SIlan Elias 		clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
1567c4bf98b2SIlan Elias 	}
15686a2968aaSIlan Elias }
15696a2968aaSIlan Elias 
15706a2968aaSIlan Elias /* ----- NCI TX CMD worker thread ----- */
15716a2968aaSIlan Elias 
nci_cmd_work(struct work_struct * work)15726a2968aaSIlan Elias static void nci_cmd_work(struct work_struct *work)
15736a2968aaSIlan Elias {
15746a2968aaSIlan Elias 	struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work);
15756a2968aaSIlan Elias 	struct sk_buff *skb;
15766a2968aaSIlan Elias 
157724bf3304SJoe Perches 	pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt));
15786a2968aaSIlan Elias 
15796a2968aaSIlan Elias 	/* Send queued command */
15806a2968aaSIlan Elias 	if (atomic_read(&ndev->cmd_cnt)) {
15816a2968aaSIlan Elias 		skb = skb_dequeue(&ndev->cmd_q);
15826a2968aaSIlan Elias 		if (!skb)
15836a2968aaSIlan Elias 			return;
15846a2968aaSIlan Elias 
15857e8cdc97SDmitry Vyukov 		kcov_remote_start_common(skb_get_kcov_handle(skb));
15866a2968aaSIlan Elias 		atomic_dec(&ndev->cmd_cnt);
15876a2968aaSIlan Elias 
158820c239c1SJoe Perches 		pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n",
15896a2968aaSIlan Elias 			 nci_pbf(skb->data),
15906a2968aaSIlan Elias 			 nci_opcode_gid(nci_opcode(skb->data)),
15916a2968aaSIlan Elias 			 nci_opcode_oid(nci_opcode(skb->data)),
15926a2968aaSIlan Elias 			 nci_plen(skb->data));
15936a2968aaSIlan Elias 
15941095e69fSFrederic Danis 		nci_send_frame(ndev, skb);
15956a2968aaSIlan Elias 
15966a2968aaSIlan Elias 		mod_timer(&ndev->cmd_timer,
15976a2968aaSIlan Elias 			  jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
15987e8cdc97SDmitry Vyukov 		kcov_remote_stop();
15996a2968aaSIlan Elias 	}
16006a2968aaSIlan Elias }
16018a70e7f8SDave Jones 
16028a70e7f8SDave Jones MODULE_LICENSE("GPL");
1603