162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  The NFC Controller Interface is the communication protocol between an
462306a36Sopenharmony_ci *  NFC Controller (NFCC) and a Device Host (DH).
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (C) 2011 Texas Instruments, Inc.
762306a36Sopenharmony_ci *  Copyright (C) 2014 Marvell International Ltd.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *  Written by Ilan Elias <ilane@ti.com>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *  Acknowledgements:
1262306a36Sopenharmony_ci *  This file is based on hci_core.c, which was written
1362306a36Sopenharmony_ci *  by Maxim Krasnyansky.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/types.h>
2162306a36Sopenharmony_ci#include <linux/workqueue.h>
2262306a36Sopenharmony_ci#include <linux/completion.h>
2362306a36Sopenharmony_ci#include <linux/export.h>
2462306a36Sopenharmony_ci#include <linux/sched.h>
2562306a36Sopenharmony_ci#include <linux/bitops.h>
2662306a36Sopenharmony_ci#include <linux/skbuff.h>
2762306a36Sopenharmony_ci#include <linux/kcov.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include "../nfc.h"
3062306a36Sopenharmony_ci#include <net/nfc/nci.h>
3162306a36Sopenharmony_ci#include <net/nfc/nci_core.h>
3262306a36Sopenharmony_ci#include <linux/nfc.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct core_conn_create_data {
3562306a36Sopenharmony_ci	int length;
3662306a36Sopenharmony_ci	struct nci_core_conn_create_cmd *cmd;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void nci_cmd_work(struct work_struct *work);
4062306a36Sopenharmony_cistatic void nci_rx_work(struct work_struct *work);
4162306a36Sopenharmony_cistatic void nci_tx_work(struct work_struct *work);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct nci_conn_info *nci_get_conn_info_by_conn_id(struct nci_dev *ndev,
4462306a36Sopenharmony_ci						   int conn_id)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
4962306a36Sopenharmony_ci		if (conn_info->conn_id == conn_id)
5062306a36Sopenharmony_ci			return conn_info;
5162306a36Sopenharmony_ci	}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return NULL;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciint nci_get_conn_info_by_dest_type_params(struct nci_dev *ndev, u8 dest_type,
5762306a36Sopenharmony_ci					  const struct dest_spec_params *params)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	list_for_each_entry(conn_info, &ndev->conn_info_list, list) {
6262306a36Sopenharmony_ci		if (conn_info->dest_type == dest_type) {
6362306a36Sopenharmony_ci			if (!params)
6462306a36Sopenharmony_ci				return conn_info->conn_id;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci			if (params->id == conn_info->dest_params->id &&
6762306a36Sopenharmony_ci			    params->protocol == conn_info->dest_params->protocol)
6862306a36Sopenharmony_ci				return conn_info->conn_id;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return -EINVAL;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ciEXPORT_SYMBOL(nci_get_conn_info_by_dest_type_params);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* ---- NCI requests ---- */
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_civoid nci_req_complete(struct nci_dev *ndev, int result)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	if (ndev->req_status == NCI_REQ_PEND) {
8162306a36Sopenharmony_ci		ndev->req_result = result;
8262306a36Sopenharmony_ci		ndev->req_status = NCI_REQ_DONE;
8362306a36Sopenharmony_ci		complete(&ndev->req_completion);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciEXPORT_SYMBOL(nci_req_complete);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void nci_req_cancel(struct nci_dev *ndev, int err)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	if (ndev->req_status == NCI_REQ_PEND) {
9162306a36Sopenharmony_ci		ndev->req_result = err;
9262306a36Sopenharmony_ci		ndev->req_status = NCI_REQ_CANCELED;
9362306a36Sopenharmony_ci		complete(&ndev->req_completion);
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/* Execute request and wait for completion. */
9862306a36Sopenharmony_cistatic int __nci_request(struct nci_dev *ndev,
9962306a36Sopenharmony_ci			 void (*req)(struct nci_dev *ndev, const void *opt),
10062306a36Sopenharmony_ci			 const void *opt, __u32 timeout)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	int rc = 0;
10362306a36Sopenharmony_ci	long completion_rc;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	ndev->req_status = NCI_REQ_PEND;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	reinit_completion(&ndev->req_completion);
10862306a36Sopenharmony_ci	req(ndev, opt);
10962306a36Sopenharmony_ci	completion_rc =
11062306a36Sopenharmony_ci		wait_for_completion_interruptible_timeout(&ndev->req_completion,
11162306a36Sopenharmony_ci							  timeout);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	pr_debug("wait_for_completion return %ld\n", completion_rc);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (completion_rc > 0) {
11662306a36Sopenharmony_ci		switch (ndev->req_status) {
11762306a36Sopenharmony_ci		case NCI_REQ_DONE:
11862306a36Sopenharmony_ci			rc = nci_to_errno(ndev->req_result);
11962306a36Sopenharmony_ci			break;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		case NCI_REQ_CANCELED:
12262306a36Sopenharmony_ci			rc = -ndev->req_result;
12362306a36Sopenharmony_ci			break;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		default:
12662306a36Sopenharmony_ci			rc = -ETIMEDOUT;
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		pr_err("wait_for_completion_interruptible_timeout failed %ld\n",
13162306a36Sopenharmony_ci		       completion_rc);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci		rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc));
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ndev->req_status = ndev->req_result = 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return rc;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciinline int nci_request(struct nci_dev *ndev,
14262306a36Sopenharmony_ci		       void (*req)(struct nci_dev *ndev,
14362306a36Sopenharmony_ci				   const void *opt),
14462306a36Sopenharmony_ci		       const void *opt, __u32 timeout)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	int rc;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Serialize all requests */
14962306a36Sopenharmony_ci	mutex_lock(&ndev->req_lock);
15062306a36Sopenharmony_ci	/* check the state after obtaing the lock against any races
15162306a36Sopenharmony_ci	 * from nci_close_device when the device gets removed.
15262306a36Sopenharmony_ci	 */
15362306a36Sopenharmony_ci	if (test_bit(NCI_UP, &ndev->flags))
15462306a36Sopenharmony_ci		rc = __nci_request(ndev, req, opt, timeout);
15562306a36Sopenharmony_ci	else
15662306a36Sopenharmony_ci		rc = -ENETDOWN;
15762306a36Sopenharmony_ci	mutex_unlock(&ndev->req_lock);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return rc;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void nci_reset_req(struct nci_dev *ndev, const void *opt)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct nci_core_reset_cmd cmd;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	cmd.reset_type = NCI_RESET_TYPE_RESET_CONFIG;
16762306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 1, &cmd);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void nci_init_req(struct nci_dev *ndev, const void *opt)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	u8 plen = 0;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (opt)
17562306a36Sopenharmony_ci		plen = sizeof(struct nci_core_init_v2_cmd);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, plen, opt);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic void nci_init_complete_req(struct nci_dev *ndev, const void *opt)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct nci_rf_disc_map_cmd cmd;
18362306a36Sopenharmony_ci	struct disc_map_config *cfg = cmd.mapping_configs;
18462306a36Sopenharmony_ci	__u8 *num = &cmd.num_mapping_configs;
18562306a36Sopenharmony_ci	int i;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* set rf mapping configurations */
18862306a36Sopenharmony_ci	*num = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* by default mapping is set to NCI_RF_INTERFACE_FRAME */
19162306a36Sopenharmony_ci	for (i = 0; i < ndev->num_supported_rf_interfaces; i++) {
19262306a36Sopenharmony_ci		if (ndev->supported_rf_interfaces[i] ==
19362306a36Sopenharmony_ci		    NCI_RF_INTERFACE_ISO_DEP) {
19462306a36Sopenharmony_ci			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
19562306a36Sopenharmony_ci			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
19662306a36Sopenharmony_ci				NCI_DISC_MAP_MODE_LISTEN;
19762306a36Sopenharmony_ci			cfg[*num].rf_interface = NCI_RF_INTERFACE_ISO_DEP;
19862306a36Sopenharmony_ci			(*num)++;
19962306a36Sopenharmony_ci		} else if (ndev->supported_rf_interfaces[i] ==
20062306a36Sopenharmony_ci			   NCI_RF_INTERFACE_NFC_DEP) {
20162306a36Sopenharmony_ci			cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
20262306a36Sopenharmony_ci			cfg[*num].mode = NCI_DISC_MAP_MODE_POLL |
20362306a36Sopenharmony_ci				NCI_DISC_MAP_MODE_LISTEN;
20462306a36Sopenharmony_ci			cfg[*num].rf_interface = NCI_RF_INTERFACE_NFC_DEP;
20562306a36Sopenharmony_ci			(*num)++;
20662306a36Sopenharmony_ci		}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci		if (*num == NCI_MAX_NUM_MAPPING_CONFIGS)
20962306a36Sopenharmony_ci			break;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD,
21362306a36Sopenharmony_ci		     (1 + ((*num) * sizeof(struct disc_map_config))), &cmd);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistruct nci_set_config_param {
21762306a36Sopenharmony_ci	__u8		id;
21862306a36Sopenharmony_ci	size_t		len;
21962306a36Sopenharmony_ci	const __u8	*val;
22062306a36Sopenharmony_ci};
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void nci_set_config_req(struct nci_dev *ndev, const void *opt)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	const struct nci_set_config_param *param = opt;
22562306a36Sopenharmony_ci	struct nci_core_set_config_cmd cmd;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	BUG_ON(param->len > NCI_MAX_PARAM_LEN);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	cmd.num_params = 1;
23062306a36Sopenharmony_ci	cmd.param.id = param->id;
23162306a36Sopenharmony_ci	cmd.param.len = param->len;
23262306a36Sopenharmony_ci	memcpy(cmd.param.val, param->val, param->len);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistruct nci_rf_discover_param {
23862306a36Sopenharmony_ci	__u32	im_protocols;
23962306a36Sopenharmony_ci	__u32	tm_protocols;
24062306a36Sopenharmony_ci};
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void nci_rf_discover_req(struct nci_dev *ndev, const void *opt)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	const struct nci_rf_discover_param *param = opt;
24562306a36Sopenharmony_ci	struct nci_rf_disc_cmd cmd;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	cmd.num_disc_configs = 0;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
25062306a36Sopenharmony_ci	    (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
25162306a36Sopenharmony_ci	     param->im_protocols & NFC_PROTO_MIFARE_MASK ||
25262306a36Sopenharmony_ci	     param->im_protocols & NFC_PROTO_ISO14443_MASK ||
25362306a36Sopenharmony_ci	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
25462306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
25562306a36Sopenharmony_ci			NCI_NFC_A_PASSIVE_POLL_MODE;
25662306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
25762306a36Sopenharmony_ci		cmd.num_disc_configs++;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
26162306a36Sopenharmony_ci	    (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
26262306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
26362306a36Sopenharmony_ci			NCI_NFC_B_PASSIVE_POLL_MODE;
26462306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
26562306a36Sopenharmony_ci		cmd.num_disc_configs++;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
26962306a36Sopenharmony_ci	    (param->im_protocols & NFC_PROTO_FELICA_MASK ||
27062306a36Sopenharmony_ci	     param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
27162306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
27262306a36Sopenharmony_ci			NCI_NFC_F_PASSIVE_POLL_MODE;
27362306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
27462306a36Sopenharmony_ci		cmd.num_disc_configs++;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
27862306a36Sopenharmony_ci	    (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
27962306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
28062306a36Sopenharmony_ci			NCI_NFC_V_PASSIVE_POLL_MODE;
28162306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
28262306a36Sopenharmony_ci		cmd.num_disc_configs++;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
28662306a36Sopenharmony_ci	    (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
28762306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
28862306a36Sopenharmony_ci			NCI_NFC_A_PASSIVE_LISTEN_MODE;
28962306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
29062306a36Sopenharmony_ci		cmd.num_disc_configs++;
29162306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
29262306a36Sopenharmony_ci			NCI_NFC_F_PASSIVE_LISTEN_MODE;
29362306a36Sopenharmony_ci		cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
29462306a36Sopenharmony_ci		cmd.num_disc_configs++;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
29862306a36Sopenharmony_ci		     (1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
29962306a36Sopenharmony_ci		     &cmd);
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistruct nci_rf_discover_select_param {
30362306a36Sopenharmony_ci	__u8	rf_discovery_id;
30462306a36Sopenharmony_ci	__u8	rf_protocol;
30562306a36Sopenharmony_ci};
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void nci_rf_discover_select_req(struct nci_dev *ndev, const void *opt)
30862306a36Sopenharmony_ci{
30962306a36Sopenharmony_ci	const struct nci_rf_discover_select_param *param = opt;
31062306a36Sopenharmony_ci	struct nci_rf_discover_select_cmd cmd;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	cmd.rf_discovery_id = param->rf_discovery_id;
31362306a36Sopenharmony_ci	cmd.rf_protocol = param->rf_protocol;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	switch (cmd.rf_protocol) {
31662306a36Sopenharmony_ci	case NCI_RF_PROTOCOL_ISO_DEP:
31762306a36Sopenharmony_ci		cmd.rf_interface = NCI_RF_INTERFACE_ISO_DEP;
31862306a36Sopenharmony_ci		break;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	case NCI_RF_PROTOCOL_NFC_DEP:
32162306a36Sopenharmony_ci		cmd.rf_interface = NCI_RF_INTERFACE_NFC_DEP;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	default:
32562306a36Sopenharmony_ci		cmd.rf_interface = NCI_RF_INTERFACE_FRAME;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_SELECT_CMD,
33062306a36Sopenharmony_ci		     sizeof(struct nci_rf_discover_select_cmd), &cmd);
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void nci_rf_deactivate_req(struct nci_dev *ndev, const void *opt)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct nci_rf_deactivate_cmd cmd;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	cmd.type = (unsigned long)opt;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
34062306a36Sopenharmony_ci		     sizeof(struct nci_rf_deactivate_cmd), &cmd);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistruct nci_cmd_param {
34462306a36Sopenharmony_ci	__u16 opcode;
34562306a36Sopenharmony_ci	size_t len;
34662306a36Sopenharmony_ci	const __u8 *payload;
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic void nci_generic_req(struct nci_dev *ndev, const void *opt)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	const struct nci_cmd_param *param = opt;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	nci_send_cmd(ndev, param->opcode, param->len, param->payload);
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciint nci_prop_cmd(struct nci_dev *ndev, __u8 oid, size_t len, const __u8 *payload)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct nci_cmd_param param;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	param.opcode = nci_opcode_pack(NCI_GID_PROPRIETARY, oid);
36162306a36Sopenharmony_ci	param.len = len;
36262306a36Sopenharmony_ci	param.payload = payload;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return __nci_request(ndev, nci_generic_req, &param,
36562306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ciEXPORT_SYMBOL(nci_prop_cmd);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ciint nci_core_cmd(struct nci_dev *ndev, __u16 opcode, size_t len,
37062306a36Sopenharmony_ci		 const __u8 *payload)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	struct nci_cmd_param param;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	param.opcode = opcode;
37562306a36Sopenharmony_ci	param.len = len;
37662306a36Sopenharmony_ci	param.payload = payload;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	return __nci_request(ndev, nci_generic_req, &param,
37962306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ciEXPORT_SYMBOL(nci_core_cmd);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ciint nci_core_reset(struct nci_dev *ndev)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	return __nci_request(ndev, nci_reset_req, (void *)0,
38662306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_RESET_TIMEOUT));
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_core_reset);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ciint nci_core_init(struct nci_dev *ndev)
39162306a36Sopenharmony_ci{
39262306a36Sopenharmony_ci	return __nci_request(ndev, nci_init_req, (void *)0,
39362306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_INIT_TIMEOUT));
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ciEXPORT_SYMBOL(nci_core_init);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_cistruct nci_loopback_data {
39862306a36Sopenharmony_ci	u8 conn_id;
39962306a36Sopenharmony_ci	struct sk_buff *data;
40062306a36Sopenharmony_ci};
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic void nci_send_data_req(struct nci_dev *ndev, const void *opt)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	const struct nci_loopback_data *data = opt;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	nci_send_data(ndev, data->conn_id, data->data);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cistatic void nci_nfcc_loopback_cb(void *context, struct sk_buff *skb, int err)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct nci_dev *ndev = (struct nci_dev *)context;
41262306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
41562306a36Sopenharmony_ci	if (!conn_info) {
41662306a36Sopenharmony_ci		nci_req_complete(ndev, NCI_STATUS_REJECTED);
41762306a36Sopenharmony_ci		return;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	conn_info->rx_skb = skb;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	nci_req_complete(ndev, NCI_STATUS_OK);
42362306a36Sopenharmony_ci}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ciint nci_nfcc_loopback(struct nci_dev *ndev, const void *data, size_t data_len,
42662306a36Sopenharmony_ci		      struct sk_buff **resp)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int r;
42962306a36Sopenharmony_ci	struct nci_loopback_data loopback_data;
43062306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
43162306a36Sopenharmony_ci	struct sk_buff *skb;
43262306a36Sopenharmony_ci	int conn_id = nci_get_conn_info_by_dest_type_params(ndev,
43362306a36Sopenharmony_ci					NCI_DESTINATION_NFCC_LOOPBACK, NULL);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (conn_id < 0) {
43662306a36Sopenharmony_ci		r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCC_LOOPBACK,
43762306a36Sopenharmony_ci					 0, 0, NULL);
43862306a36Sopenharmony_ci		if (r != NCI_STATUS_OK)
43962306a36Sopenharmony_ci			return r;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		conn_id = nci_get_conn_info_by_dest_type_params(ndev,
44262306a36Sopenharmony_ci					NCI_DESTINATION_NFCC_LOOPBACK,
44362306a36Sopenharmony_ci					NULL);
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id);
44762306a36Sopenharmony_ci	if (!conn_info)
44862306a36Sopenharmony_ci		return -EPROTO;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	/* store cb and context to be used on receiving data */
45162306a36Sopenharmony_ci	conn_info->data_exchange_cb = nci_nfcc_loopback_cb;
45262306a36Sopenharmony_ci	conn_info->data_exchange_cb_context = ndev;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	skb = nci_skb_alloc(ndev, NCI_DATA_HDR_SIZE + data_len, GFP_KERNEL);
45562306a36Sopenharmony_ci	if (!skb)
45662306a36Sopenharmony_ci		return -ENOMEM;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	skb_reserve(skb, NCI_DATA_HDR_SIZE);
45962306a36Sopenharmony_ci	skb_put_data(skb, data, data_len);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	loopback_data.conn_id = conn_id;
46262306a36Sopenharmony_ci	loopback_data.data = skb;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	ndev->cur_conn_id = conn_id;
46562306a36Sopenharmony_ci	r = nci_request(ndev, nci_send_data_req, &loopback_data,
46662306a36Sopenharmony_ci			msecs_to_jiffies(NCI_DATA_TIMEOUT));
46762306a36Sopenharmony_ci	if (r == NCI_STATUS_OK && resp)
46862306a36Sopenharmony_ci		*resp = conn_info->rx_skb;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return r;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_nfcc_loopback);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic int nci_open_device(struct nci_dev *ndev)
47562306a36Sopenharmony_ci{
47662306a36Sopenharmony_ci	int rc = 0;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	mutex_lock(&ndev->req_lock);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	if (test_bit(NCI_UNREG, &ndev->flags)) {
48162306a36Sopenharmony_ci		rc = -ENODEV;
48262306a36Sopenharmony_ci		goto done;
48362306a36Sopenharmony_ci	}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	if (test_bit(NCI_UP, &ndev->flags)) {
48662306a36Sopenharmony_ci		rc = -EALREADY;
48762306a36Sopenharmony_ci		goto done;
48862306a36Sopenharmony_ci	}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (ndev->ops->open(ndev)) {
49162306a36Sopenharmony_ci		rc = -EIO;
49262306a36Sopenharmony_ci		goto done;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	atomic_set(&ndev->cmd_cnt, 1);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	set_bit(NCI_INIT, &ndev->flags);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (ndev->ops->init)
50062306a36Sopenharmony_ci		rc = ndev->ops->init(ndev);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (!rc) {
50362306a36Sopenharmony_ci		rc = __nci_request(ndev, nci_reset_req, (void *)0,
50462306a36Sopenharmony_ci				   msecs_to_jiffies(NCI_RESET_TIMEOUT));
50562306a36Sopenharmony_ci	}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!rc && ndev->ops->setup) {
50862306a36Sopenharmony_ci		rc = ndev->ops->setup(ndev);
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (!rc) {
51262306a36Sopenharmony_ci		struct nci_core_init_v2_cmd nci_init_v2_cmd = {
51362306a36Sopenharmony_ci			.feature1 = NCI_FEATURE_DISABLE,
51462306a36Sopenharmony_ci			.feature2 = NCI_FEATURE_DISABLE
51562306a36Sopenharmony_ci		};
51662306a36Sopenharmony_ci		const void *opt = NULL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci		if (ndev->nci_ver & NCI_VER_2_MASK)
51962306a36Sopenharmony_ci			opt = &nci_init_v2_cmd;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		rc = __nci_request(ndev, nci_init_req, opt,
52262306a36Sopenharmony_ci				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (!rc && ndev->ops->post_setup)
52662306a36Sopenharmony_ci		rc = ndev->ops->post_setup(ndev);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (!rc) {
52962306a36Sopenharmony_ci		rc = __nci_request(ndev, nci_init_complete_req, (void *)0,
53062306a36Sopenharmony_ci				   msecs_to_jiffies(NCI_INIT_TIMEOUT));
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	clear_bit(NCI_INIT, &ndev->flags);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (!rc) {
53662306a36Sopenharmony_ci		set_bit(NCI_UP, &ndev->flags);
53762306a36Sopenharmony_ci		nci_clear_target_list(ndev);
53862306a36Sopenharmony_ci		atomic_set(&ndev->state, NCI_IDLE);
53962306a36Sopenharmony_ci	} else {
54062306a36Sopenharmony_ci		/* Init failed, cleanup */
54162306a36Sopenharmony_ci		skb_queue_purge(&ndev->cmd_q);
54262306a36Sopenharmony_ci		skb_queue_purge(&ndev->rx_q);
54362306a36Sopenharmony_ci		skb_queue_purge(&ndev->tx_q);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		ndev->ops->close(ndev);
54662306a36Sopenharmony_ci		ndev->flags &= BIT(NCI_UNREG);
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cidone:
55062306a36Sopenharmony_ci	mutex_unlock(&ndev->req_lock);
55162306a36Sopenharmony_ci	return rc;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic int nci_close_device(struct nci_dev *ndev)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	nci_req_cancel(ndev, ENODEV);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	/* This mutex needs to be held as a barrier for
55962306a36Sopenharmony_ci	 * caller nci_unregister_device
56062306a36Sopenharmony_ci	 */
56162306a36Sopenharmony_ci	mutex_lock(&ndev->req_lock);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
56462306a36Sopenharmony_ci		/* Need to flush the cmd wq in case
56562306a36Sopenharmony_ci		 * there is a queued/running cmd_work
56662306a36Sopenharmony_ci		 */
56762306a36Sopenharmony_ci		flush_workqueue(ndev->cmd_wq);
56862306a36Sopenharmony_ci		del_timer_sync(&ndev->cmd_timer);
56962306a36Sopenharmony_ci		del_timer_sync(&ndev->data_timer);
57062306a36Sopenharmony_ci		mutex_unlock(&ndev->req_lock);
57162306a36Sopenharmony_ci		return 0;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	/* Drop RX and TX queues */
57562306a36Sopenharmony_ci	skb_queue_purge(&ndev->rx_q);
57662306a36Sopenharmony_ci	skb_queue_purge(&ndev->tx_q);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/* Flush RX and TX wq */
57962306a36Sopenharmony_ci	flush_workqueue(ndev->rx_wq);
58062306a36Sopenharmony_ci	flush_workqueue(ndev->tx_wq);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Reset device */
58362306a36Sopenharmony_ci	skb_queue_purge(&ndev->cmd_q);
58462306a36Sopenharmony_ci	atomic_set(&ndev->cmd_cnt, 1);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	set_bit(NCI_INIT, &ndev->flags);
58762306a36Sopenharmony_ci	__nci_request(ndev, nci_reset_req, (void *)0,
58862306a36Sopenharmony_ci		      msecs_to_jiffies(NCI_RESET_TIMEOUT));
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* After this point our queues are empty
59162306a36Sopenharmony_ci	 * and no works are scheduled.
59262306a36Sopenharmony_ci	 */
59362306a36Sopenharmony_ci	ndev->ops->close(ndev);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	clear_bit(NCI_INIT, &ndev->flags);
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	/* Flush cmd wq */
59862306a36Sopenharmony_ci	flush_workqueue(ndev->cmd_wq);
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	del_timer_sync(&ndev->cmd_timer);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Clear flags except NCI_UNREG */
60362306a36Sopenharmony_ci	ndev->flags &= BIT(NCI_UNREG);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	mutex_unlock(&ndev->req_lock);
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci/* NCI command timer function */
61162306a36Sopenharmony_cistatic void nci_cmd_timer(struct timer_list *t)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct nci_dev *ndev = from_timer(ndev, t, cmd_timer);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	atomic_set(&ndev->cmd_cnt, 1);
61662306a36Sopenharmony_ci	queue_work(ndev->cmd_wq, &ndev->cmd_work);
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci/* NCI data exchange timer function */
62062306a36Sopenharmony_cistatic void nci_data_timer(struct timer_list *t)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct nci_dev *ndev = from_timer(ndev, t, data_timer);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	set_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
62562306a36Sopenharmony_ci	queue_work(ndev->rx_wq, &ndev->rx_work);
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic int nci_dev_up(struct nfc_dev *nfc_dev)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return nci_open_device(ndev);
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_cistatic int nci_dev_down(struct nfc_dev *nfc_dev)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return nci_close_device(ndev);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ciint nci_set_config(struct nci_dev *ndev, __u8 id, size_t len, const __u8 *val)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct nci_set_config_param param;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!val || !len)
64762306a36Sopenharmony_ci		return 0;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	param.id = id;
65062306a36Sopenharmony_ci	param.len = len;
65162306a36Sopenharmony_ci	param.val = val;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	return __nci_request(ndev, nci_set_config_req, &param,
65462306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ciEXPORT_SYMBOL(nci_set_config);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_cistatic void nci_nfcee_discover_req(struct nci_dev *ndev, const void *opt)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	struct nci_nfcee_discover_cmd cmd;
66162306a36Sopenharmony_ci	__u8 action = (unsigned long)opt;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	cmd.discovery_action = action;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_NFCEE_DISCOVER_CMD, 1, &cmd);
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ciint nci_nfcee_discover(struct nci_dev *ndev, u8 action)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	unsigned long opt = action;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	return __nci_request(ndev, nci_nfcee_discover_req, (void *)opt,
67362306a36Sopenharmony_ci				msecs_to_jiffies(NCI_CMD_TIMEOUT));
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ciEXPORT_SYMBOL(nci_nfcee_discover);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void nci_nfcee_mode_set_req(struct nci_dev *ndev, const void *opt)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	const struct nci_nfcee_mode_set_cmd *cmd = opt;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_NFCEE_MODE_SET_CMD,
68262306a36Sopenharmony_ci		     sizeof(struct nci_nfcee_mode_set_cmd), cmd);
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ciint nci_nfcee_mode_set(struct nci_dev *ndev, u8 nfcee_id, u8 nfcee_mode)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct nci_nfcee_mode_set_cmd cmd;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	cmd.nfcee_id = nfcee_id;
69062306a36Sopenharmony_ci	cmd.nfcee_mode = nfcee_mode;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	return __nci_request(ndev, nci_nfcee_mode_set_req, &cmd,
69362306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
69462306a36Sopenharmony_ci}
69562306a36Sopenharmony_ciEXPORT_SYMBOL(nci_nfcee_mode_set);
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_cistatic void nci_core_conn_create_req(struct nci_dev *ndev, const void *opt)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	const struct core_conn_create_data *data = opt;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, data->length, data->cmd);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ciint nci_core_conn_create(struct nci_dev *ndev, u8 destination_type,
70562306a36Sopenharmony_ci			 u8 number_destination_params,
70662306a36Sopenharmony_ci			 size_t params_len,
70762306a36Sopenharmony_ci			 const struct core_conn_create_dest_spec_params *params)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	int r;
71062306a36Sopenharmony_ci	struct nci_core_conn_create_cmd *cmd;
71162306a36Sopenharmony_ci	struct core_conn_create_data data;
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	data.length = params_len + sizeof(struct nci_core_conn_create_cmd);
71462306a36Sopenharmony_ci	cmd = kzalloc(data.length, GFP_KERNEL);
71562306a36Sopenharmony_ci	if (!cmd)
71662306a36Sopenharmony_ci		return -ENOMEM;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	cmd->destination_type = destination_type;
71962306a36Sopenharmony_ci	cmd->number_destination_params = number_destination_params;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	data.cmd = cmd;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	if (params) {
72462306a36Sopenharmony_ci		memcpy(cmd->params, params, params_len);
72562306a36Sopenharmony_ci		if (params->length > 0)
72662306a36Sopenharmony_ci			memcpy(&ndev->cur_params,
72762306a36Sopenharmony_ci			       &params->value[DEST_SPEC_PARAMS_ID_INDEX],
72862306a36Sopenharmony_ci			       sizeof(struct dest_spec_params));
72962306a36Sopenharmony_ci		else
73062306a36Sopenharmony_ci			ndev->cur_params.id = 0;
73162306a36Sopenharmony_ci	} else {
73262306a36Sopenharmony_ci		ndev->cur_params.id = 0;
73362306a36Sopenharmony_ci	}
73462306a36Sopenharmony_ci	ndev->cur_dest_type = destination_type;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	r = __nci_request(ndev, nci_core_conn_create_req, &data,
73762306a36Sopenharmony_ci			  msecs_to_jiffies(NCI_CMD_TIMEOUT));
73862306a36Sopenharmony_ci	kfree(cmd);
73962306a36Sopenharmony_ci	return r;
74062306a36Sopenharmony_ci}
74162306a36Sopenharmony_ciEXPORT_SYMBOL(nci_core_conn_create);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic void nci_core_conn_close_req(struct nci_dev *ndev, const void *opt)
74462306a36Sopenharmony_ci{
74562306a36Sopenharmony_ci	__u8 conn_id = (unsigned long)opt;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	nci_send_cmd(ndev, NCI_OP_CORE_CONN_CLOSE_CMD, 1, &conn_id);
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ciint nci_core_conn_close(struct nci_dev *ndev, u8 conn_id)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	unsigned long opt = conn_id;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ndev->cur_conn_id = conn_id;
75562306a36Sopenharmony_ci	return __nci_request(ndev, nci_core_conn_close_req, (void *)opt,
75662306a36Sopenharmony_ci			     msecs_to_jiffies(NCI_CMD_TIMEOUT));
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_core_conn_close);
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
76162306a36Sopenharmony_ci{
76262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
76362306a36Sopenharmony_ci	struct nci_set_config_param param;
76462306a36Sopenharmony_ci	int rc;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
76762306a36Sopenharmony_ci	if ((param.val == NULL) || (param.len == 0))
76862306a36Sopenharmony_ci		return 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	if (param.len > NFC_MAX_GT_LEN)
77162306a36Sopenharmony_ci		return -EINVAL;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	param.id = NCI_PN_ATR_REQ_GEN_BYTES;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	rc = nci_request(ndev, nci_set_config_req, &param,
77662306a36Sopenharmony_ci			 msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
77762306a36Sopenharmony_ci	if (rc)
77862306a36Sopenharmony_ci		return rc;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	param.id = NCI_LN_ATR_RES_GEN_BYTES;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	return nci_request(ndev, nci_set_config_req, &param,
78362306a36Sopenharmony_ci			   msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
78462306a36Sopenharmony_ci}
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
78962306a36Sopenharmony_ci	int rc;
79062306a36Sopenharmony_ci	__u8 val;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
79562306a36Sopenharmony_ci	if (rc)
79662306a36Sopenharmony_ci		return rc;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
80162306a36Sopenharmony_ci	if (rc)
80262306a36Sopenharmony_ci		return rc;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_cistatic int nci_start_poll(struct nfc_dev *nfc_dev,
81062306a36Sopenharmony_ci			  __u32 im_protocols, __u32 tm_protocols)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
81362306a36Sopenharmony_ci	struct nci_rf_discover_param param;
81462306a36Sopenharmony_ci	int rc;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
81762306a36Sopenharmony_ci	    (atomic_read(&ndev->state) == NCI_W4_ALL_DISCOVERIES)) {
81862306a36Sopenharmony_ci		pr_err("unable to start poll, since poll is already active\n");
81962306a36Sopenharmony_ci		return -EBUSY;
82062306a36Sopenharmony_ci	}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	if (ndev->target_active_prot) {
82362306a36Sopenharmony_ci		pr_err("there is an active target\n");
82462306a36Sopenharmony_ci		return -EBUSY;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if ((atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) ||
82862306a36Sopenharmony_ci	    (atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
82962306a36Sopenharmony_ci		pr_debug("target active or w4 select, implicitly deactivate\n");
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci		rc = nci_request(ndev, nci_rf_deactivate_req,
83262306a36Sopenharmony_ci				 (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE,
83362306a36Sopenharmony_ci				 msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
83462306a36Sopenharmony_ci		if (rc)
83562306a36Sopenharmony_ci			return -EBUSY;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
83962306a36Sopenharmony_ci		rc = nci_set_local_general_bytes(nfc_dev);
84062306a36Sopenharmony_ci		if (rc) {
84162306a36Sopenharmony_ci			pr_err("failed to set local general bytes\n");
84262306a36Sopenharmony_ci			return rc;
84362306a36Sopenharmony_ci		}
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
84762306a36Sopenharmony_ci		rc = nci_set_listen_parameters(nfc_dev);
84862306a36Sopenharmony_ci		if (rc)
84962306a36Sopenharmony_ci			pr_err("failed to set listen parameters\n");
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	param.im_protocols = im_protocols;
85362306a36Sopenharmony_ci	param.tm_protocols = tm_protocols;
85462306a36Sopenharmony_ci	rc = nci_request(ndev, nci_rf_discover_req, &param,
85562306a36Sopenharmony_ci			 msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	if (!rc)
85862306a36Sopenharmony_ci		ndev->poll_prots = im_protocols;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	return rc;
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic void nci_stop_poll(struct nfc_dev *nfc_dev)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	if ((atomic_read(&ndev->state) != NCI_DISCOVERY) &&
86862306a36Sopenharmony_ci	    (atomic_read(&ndev->state) != NCI_W4_ALL_DISCOVERIES)) {
86962306a36Sopenharmony_ci		pr_err("unable to stop poll, since poll is not active\n");
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	nci_request(ndev, nci_rf_deactivate_req,
87462306a36Sopenharmony_ci		    (void *)NCI_DEACTIVATE_TYPE_IDLE_MODE,
87562306a36Sopenharmony_ci		    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int nci_activate_target(struct nfc_dev *nfc_dev,
87962306a36Sopenharmony_ci			       struct nfc_target *target, __u32 protocol)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
88262306a36Sopenharmony_ci	struct nci_rf_discover_select_param param;
88362306a36Sopenharmony_ci	const struct nfc_target *nci_target = NULL;
88462306a36Sopenharmony_ci	int i;
88562306a36Sopenharmony_ci	int rc = 0;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	pr_debug("target_idx %d, protocol 0x%x\n", target->idx, protocol);
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if ((atomic_read(&ndev->state) != NCI_W4_HOST_SELECT) &&
89062306a36Sopenharmony_ci	    (atomic_read(&ndev->state) != NCI_POLL_ACTIVE)) {
89162306a36Sopenharmony_ci		pr_err("there is no available target to activate\n");
89262306a36Sopenharmony_ci		return -EINVAL;
89362306a36Sopenharmony_ci	}
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (ndev->target_active_prot) {
89662306a36Sopenharmony_ci		pr_err("there is already an active target\n");
89762306a36Sopenharmony_ci		return -EBUSY;
89862306a36Sopenharmony_ci	}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	for (i = 0; i < ndev->n_targets; i++) {
90162306a36Sopenharmony_ci		if (ndev->targets[i].idx == target->idx) {
90262306a36Sopenharmony_ci			nci_target = &ndev->targets[i];
90362306a36Sopenharmony_ci			break;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci	}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (!nci_target) {
90862306a36Sopenharmony_ci		pr_err("unable to find the selected target\n");
90962306a36Sopenharmony_ci		return -EINVAL;
91062306a36Sopenharmony_ci	}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (protocol >= NFC_PROTO_MAX) {
91362306a36Sopenharmony_ci		pr_err("the requested nfc protocol is invalid\n");
91462306a36Sopenharmony_ci		return -EINVAL;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!(nci_target->supported_protocols & (1 << protocol))) {
91862306a36Sopenharmony_ci		pr_err("target does not support the requested protocol 0x%x\n",
91962306a36Sopenharmony_ci		       protocol);
92062306a36Sopenharmony_ci		return -EINVAL;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (atomic_read(&ndev->state) == NCI_W4_HOST_SELECT) {
92462306a36Sopenharmony_ci		param.rf_discovery_id = nci_target->logical_idx;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		if (protocol == NFC_PROTO_JEWEL)
92762306a36Sopenharmony_ci			param.rf_protocol = NCI_RF_PROTOCOL_T1T;
92862306a36Sopenharmony_ci		else if (protocol == NFC_PROTO_MIFARE)
92962306a36Sopenharmony_ci			param.rf_protocol = NCI_RF_PROTOCOL_T2T;
93062306a36Sopenharmony_ci		else if (protocol == NFC_PROTO_FELICA)
93162306a36Sopenharmony_ci			param.rf_protocol = NCI_RF_PROTOCOL_T3T;
93262306a36Sopenharmony_ci		else if (protocol == NFC_PROTO_ISO14443 ||
93362306a36Sopenharmony_ci			 protocol == NFC_PROTO_ISO14443_B)
93462306a36Sopenharmony_ci			param.rf_protocol = NCI_RF_PROTOCOL_ISO_DEP;
93562306a36Sopenharmony_ci		else
93662306a36Sopenharmony_ci			param.rf_protocol = NCI_RF_PROTOCOL_NFC_DEP;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci		rc = nci_request(ndev, nci_rf_discover_select_req, &param,
93962306a36Sopenharmony_ci				 msecs_to_jiffies(NCI_RF_DISC_SELECT_TIMEOUT));
94062306a36Sopenharmony_ci	}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	if (!rc)
94362306a36Sopenharmony_ci		ndev->target_active_prot = protocol;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return rc;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void nci_deactivate_target(struct nfc_dev *nfc_dev,
94962306a36Sopenharmony_ci				  struct nfc_target *target,
95062306a36Sopenharmony_ci				  __u8 mode)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
95362306a36Sopenharmony_ci	unsigned long nci_mode = NCI_DEACTIVATE_TYPE_IDLE_MODE;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	if (!ndev->target_active_prot) {
95662306a36Sopenharmony_ci		pr_err("unable to deactivate target, no active target\n");
95762306a36Sopenharmony_ci		return;
95862306a36Sopenharmony_ci	}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	ndev->target_active_prot = 0;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	switch (mode) {
96362306a36Sopenharmony_ci	case NFC_TARGET_MODE_SLEEP:
96462306a36Sopenharmony_ci		nci_mode = NCI_DEACTIVATE_TYPE_SLEEP_MODE;
96562306a36Sopenharmony_ci		break;
96662306a36Sopenharmony_ci	}
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
96962306a36Sopenharmony_ci		nci_request(ndev, nci_rf_deactivate_req, (void *)nci_mode,
97062306a36Sopenharmony_ci			    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
97562306a36Sopenharmony_ci			   __u8 comm_mode, __u8 *gb, size_t gb_len)
97662306a36Sopenharmony_ci{
97762306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
97862306a36Sopenharmony_ci	int rc;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	pr_debug("target_idx %d, comm_mode %d\n", target->idx, comm_mode);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	rc = nci_activate_target(nfc_dev, target, NFC_PROTO_NFC_DEP);
98362306a36Sopenharmony_ci	if (rc)
98462306a36Sopenharmony_ci		return rc;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci	rc = nfc_set_remote_general_bytes(nfc_dev, ndev->remote_gb,
98762306a36Sopenharmony_ci					  ndev->remote_gb_len);
98862306a36Sopenharmony_ci	if (!rc)
98962306a36Sopenharmony_ci		rc = nfc_dep_link_is_up(nfc_dev, target->idx, NFC_COMM_PASSIVE,
99062306a36Sopenharmony_ci					NFC_RF_INITIATOR);
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	return rc;
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_cistatic int nci_dep_link_down(struct nfc_dev *nfc_dev)
99662306a36Sopenharmony_ci{
99762306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
99862306a36Sopenharmony_ci	int rc;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
100162306a36Sopenharmony_ci		nci_deactivate_target(nfc_dev, NULL, NCI_DEACTIVATE_TYPE_IDLE_MODE);
100262306a36Sopenharmony_ci	} else {
100362306a36Sopenharmony_ci		if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
100462306a36Sopenharmony_ci		    atomic_read(&ndev->state) == NCI_DISCOVERY) {
100562306a36Sopenharmony_ci			nci_request(ndev, nci_rf_deactivate_req, (void *)0,
100662306a36Sopenharmony_ci				    msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
100762306a36Sopenharmony_ci		}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci		rc = nfc_tm_deactivated(nfc_dev);
101062306a36Sopenharmony_ci		if (rc)
101162306a36Sopenharmony_ci			pr_err("error when signaling tm deactivation\n");
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	return 0;
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_cistatic int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
101962306a36Sopenharmony_ci			  struct sk_buff *skb,
102062306a36Sopenharmony_ci			  data_exchange_cb_t cb, void *cb_context)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
102362306a36Sopenharmony_ci	int rc;
102462306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	conn_info = ndev->rf_conn_info;
102762306a36Sopenharmony_ci	if (!conn_info)
102862306a36Sopenharmony_ci		return -EPROTO;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	pr_debug("target_idx %d, len %d\n", target->idx, skb->len);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (!ndev->target_active_prot) {
103362306a36Sopenharmony_ci		pr_err("unable to exchange data, no active target\n");
103462306a36Sopenharmony_ci		return -EINVAL;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags))
103862306a36Sopenharmony_ci		return -EBUSY;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* store cb and context to be used on receiving data */
104162306a36Sopenharmony_ci	conn_info->data_exchange_cb = cb;
104262306a36Sopenharmony_ci	conn_info->data_exchange_cb_context = cb_context;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
104562306a36Sopenharmony_ci	if (rc)
104662306a36Sopenharmony_ci		clear_bit(NCI_DATA_EXCHANGE, &ndev->flags);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return rc;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
105262306a36Sopenharmony_ci{
105362306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
105462306a36Sopenharmony_ci	int rc;
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
105762306a36Sopenharmony_ci	if (rc)
105862306a36Sopenharmony_ci		pr_err("unable to send data\n");
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	return rc;
106162306a36Sopenharmony_ci}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_cistatic int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
106462306a36Sopenharmony_ci{
106562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	if (ndev->ops->enable_se)
106862306a36Sopenharmony_ci		return ndev->ops->enable_se(ndev, se_idx);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return 0;
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_cistatic int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
107462306a36Sopenharmony_ci{
107562306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if (ndev->ops->disable_se)
107862306a36Sopenharmony_ci		return ndev->ops->disable_se(ndev, se_idx);
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci	return 0;
108162306a36Sopenharmony_ci}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_cistatic int nci_discover_se(struct nfc_dev *nfc_dev)
108462306a36Sopenharmony_ci{
108562306a36Sopenharmony_ci	int r;
108662306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	if (ndev->ops->discover_se) {
108962306a36Sopenharmony_ci		r = nci_nfcee_discover(ndev, NCI_NFCEE_DISCOVERY_ACTION_ENABLE);
109062306a36Sopenharmony_ci		if (r != NCI_STATUS_OK)
109162306a36Sopenharmony_ci			return -EPROTO;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		return ndev->ops->discover_se(ndev);
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	return 0;
109762306a36Sopenharmony_ci}
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_cistatic int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
110062306a36Sopenharmony_ci		     u8 *apdu, size_t apdu_length,
110162306a36Sopenharmony_ci		     se_io_cb_t cb, void *cb_context)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	if (ndev->ops->se_io)
110662306a36Sopenharmony_ci		return ndev->ops->se_io(ndev, se_idx, apdu,
110762306a36Sopenharmony_ci				apdu_length, cb, cb_context);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	return 0;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_cistatic int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name)
111362306a36Sopenharmony_ci{
111462306a36Sopenharmony_ci	struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	if (!ndev->ops->fw_download)
111762306a36Sopenharmony_ci		return -ENOTSUPP;
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	return ndev->ops->fw_download(ndev, firmware_name);
112062306a36Sopenharmony_ci}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic const struct nfc_ops nci_nfc_ops = {
112362306a36Sopenharmony_ci	.dev_up = nci_dev_up,
112462306a36Sopenharmony_ci	.dev_down = nci_dev_down,
112562306a36Sopenharmony_ci	.start_poll = nci_start_poll,
112662306a36Sopenharmony_ci	.stop_poll = nci_stop_poll,
112762306a36Sopenharmony_ci	.dep_link_up = nci_dep_link_up,
112862306a36Sopenharmony_ci	.dep_link_down = nci_dep_link_down,
112962306a36Sopenharmony_ci	.activate_target = nci_activate_target,
113062306a36Sopenharmony_ci	.deactivate_target = nci_deactivate_target,
113162306a36Sopenharmony_ci	.im_transceive = nci_transceive,
113262306a36Sopenharmony_ci	.tm_send = nci_tm_send,
113362306a36Sopenharmony_ci	.enable_se = nci_enable_se,
113462306a36Sopenharmony_ci	.disable_se = nci_disable_se,
113562306a36Sopenharmony_ci	.discover_se = nci_discover_se,
113662306a36Sopenharmony_ci	.se_io = nci_se_io,
113762306a36Sopenharmony_ci	.fw_download = nci_fw_download,
113862306a36Sopenharmony_ci};
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci/* ---- Interface to NCI drivers ---- */
114162306a36Sopenharmony_ci/**
114262306a36Sopenharmony_ci * nci_allocate_device - allocate a new nci device
114362306a36Sopenharmony_ci *
114462306a36Sopenharmony_ci * @ops: device operations
114562306a36Sopenharmony_ci * @supported_protocols: NFC protocols supported by the device
114662306a36Sopenharmony_ci * @tx_headroom: Reserved space at beginning of skb
114762306a36Sopenharmony_ci * @tx_tailroom: Reserved space at end of skb
114862306a36Sopenharmony_ci */
114962306a36Sopenharmony_cistruct nci_dev *nci_allocate_device(const struct nci_ops *ops,
115062306a36Sopenharmony_ci				    __u32 supported_protocols,
115162306a36Sopenharmony_ci				    int tx_headroom, int tx_tailroom)
115262306a36Sopenharmony_ci{
115362306a36Sopenharmony_ci	struct nci_dev *ndev;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	pr_debug("supported_protocols 0x%x\n", supported_protocols);
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	if (!ops->open || !ops->close || !ops->send)
115862306a36Sopenharmony_ci		return NULL;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	if (!supported_protocols)
116162306a36Sopenharmony_ci		return NULL;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL);
116462306a36Sopenharmony_ci	if (!ndev)
116562306a36Sopenharmony_ci		return NULL;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci	ndev->ops = ops;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	if (ops->n_prop_ops > NCI_MAX_PROPRIETARY_CMD) {
117062306a36Sopenharmony_ci		pr_err("Too many proprietary commands: %zd\n",
117162306a36Sopenharmony_ci		       ops->n_prop_ops);
117262306a36Sopenharmony_ci		goto free_nci;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	ndev->tx_headroom = tx_headroom;
117662306a36Sopenharmony_ci	ndev->tx_tailroom = tx_tailroom;
117762306a36Sopenharmony_ci	init_completion(&ndev->req_completion);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops,
118062306a36Sopenharmony_ci					    supported_protocols,
118162306a36Sopenharmony_ci					    tx_headroom + NCI_DATA_HDR_SIZE,
118262306a36Sopenharmony_ci					    tx_tailroom);
118362306a36Sopenharmony_ci	if (!ndev->nfc_dev)
118462306a36Sopenharmony_ci		goto free_nci;
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	ndev->hci_dev = nci_hci_allocate(ndev);
118762306a36Sopenharmony_ci	if (!ndev->hci_dev)
118862306a36Sopenharmony_ci		goto free_nfc;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	nfc_set_drvdata(ndev->nfc_dev, ndev);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	return ndev;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cifree_nfc:
119562306a36Sopenharmony_ci	nfc_free_device(ndev->nfc_dev);
119662306a36Sopenharmony_cifree_nci:
119762306a36Sopenharmony_ci	kfree(ndev);
119862306a36Sopenharmony_ci	return NULL;
119962306a36Sopenharmony_ci}
120062306a36Sopenharmony_ciEXPORT_SYMBOL(nci_allocate_device);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci/**
120362306a36Sopenharmony_ci * nci_free_device - deallocate nci device
120462306a36Sopenharmony_ci *
120562306a36Sopenharmony_ci * @ndev: The nci device to deallocate
120662306a36Sopenharmony_ci */
120762306a36Sopenharmony_civoid nci_free_device(struct nci_dev *ndev)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	nfc_free_device(ndev->nfc_dev);
121062306a36Sopenharmony_ci	nci_hci_deallocate(ndev);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/* drop partial rx data packet if present */
121362306a36Sopenharmony_ci	if (ndev->rx_data_reassembly)
121462306a36Sopenharmony_ci		kfree_skb(ndev->rx_data_reassembly);
121562306a36Sopenharmony_ci	kfree(ndev);
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ciEXPORT_SYMBOL(nci_free_device);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci/**
122062306a36Sopenharmony_ci * nci_register_device - register a nci device in the nfc subsystem
122162306a36Sopenharmony_ci *
122262306a36Sopenharmony_ci * @ndev: The nci device to register
122362306a36Sopenharmony_ci */
122462306a36Sopenharmony_ciint nci_register_device(struct nci_dev *ndev)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	int rc;
122762306a36Sopenharmony_ci	struct device *dev = &ndev->nfc_dev->dev;
122862306a36Sopenharmony_ci	char name[32];
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	ndev->flags = 0;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	INIT_WORK(&ndev->cmd_work, nci_cmd_work);
123362306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev));
123462306a36Sopenharmony_ci	ndev->cmd_wq = create_singlethread_workqueue(name);
123562306a36Sopenharmony_ci	if (!ndev->cmd_wq) {
123662306a36Sopenharmony_ci		rc = -ENOMEM;
123762306a36Sopenharmony_ci		goto exit;
123862306a36Sopenharmony_ci	}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	INIT_WORK(&ndev->rx_work, nci_rx_work);
124162306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev));
124262306a36Sopenharmony_ci	ndev->rx_wq = create_singlethread_workqueue(name);
124362306a36Sopenharmony_ci	if (!ndev->rx_wq) {
124462306a36Sopenharmony_ci		rc = -ENOMEM;
124562306a36Sopenharmony_ci		goto destroy_cmd_wq_exit;
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	INIT_WORK(&ndev->tx_work, nci_tx_work);
124962306a36Sopenharmony_ci	snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev));
125062306a36Sopenharmony_ci	ndev->tx_wq = create_singlethread_workqueue(name);
125162306a36Sopenharmony_ci	if (!ndev->tx_wq) {
125262306a36Sopenharmony_ci		rc = -ENOMEM;
125362306a36Sopenharmony_ci		goto destroy_rx_wq_exit;
125462306a36Sopenharmony_ci	}
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	skb_queue_head_init(&ndev->cmd_q);
125762306a36Sopenharmony_ci	skb_queue_head_init(&ndev->rx_q);
125862306a36Sopenharmony_ci	skb_queue_head_init(&ndev->tx_q);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	timer_setup(&ndev->cmd_timer, nci_cmd_timer, 0);
126162306a36Sopenharmony_ci	timer_setup(&ndev->data_timer, nci_data_timer, 0);
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	mutex_init(&ndev->req_lock);
126462306a36Sopenharmony_ci	INIT_LIST_HEAD(&ndev->conn_info_list);
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	rc = nfc_register_device(ndev->nfc_dev);
126762306a36Sopenharmony_ci	if (rc)
126862306a36Sopenharmony_ci		goto destroy_tx_wq_exit;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	goto exit;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cidestroy_tx_wq_exit:
127362306a36Sopenharmony_ci	destroy_workqueue(ndev->tx_wq);
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_cidestroy_rx_wq_exit:
127662306a36Sopenharmony_ci	destroy_workqueue(ndev->rx_wq);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cidestroy_cmd_wq_exit:
127962306a36Sopenharmony_ci	destroy_workqueue(ndev->cmd_wq);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ciexit:
128262306a36Sopenharmony_ci	return rc;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ciEXPORT_SYMBOL(nci_register_device);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci/**
128762306a36Sopenharmony_ci * nci_unregister_device - unregister a nci device in the nfc subsystem
128862306a36Sopenharmony_ci *
128962306a36Sopenharmony_ci * @ndev: The nci device to unregister
129062306a36Sopenharmony_ci */
129162306a36Sopenharmony_civoid nci_unregister_device(struct nci_dev *ndev)
129262306a36Sopenharmony_ci{
129362306a36Sopenharmony_ci	struct nci_conn_info *conn_info, *n;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	/* This set_bit is not protected with specialized barrier,
129662306a36Sopenharmony_ci	 * However, it is fine because the mutex_lock(&ndev->req_lock);
129762306a36Sopenharmony_ci	 * in nci_close_device() will help to emit one.
129862306a36Sopenharmony_ci	 */
129962306a36Sopenharmony_ci	set_bit(NCI_UNREG, &ndev->flags);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	nci_close_device(ndev);
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	destroy_workqueue(ndev->cmd_wq);
130462306a36Sopenharmony_ci	destroy_workqueue(ndev->rx_wq);
130562306a36Sopenharmony_ci	destroy_workqueue(ndev->tx_wq);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	list_for_each_entry_safe(conn_info, n, &ndev->conn_info_list, list) {
130862306a36Sopenharmony_ci		list_del(&conn_info->list);
130962306a36Sopenharmony_ci		/* conn_info is allocated with devm_kzalloc */
131062306a36Sopenharmony_ci	}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	nfc_unregister_device(ndev->nfc_dev);
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ciEXPORT_SYMBOL(nci_unregister_device);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci/**
131762306a36Sopenharmony_ci * nci_recv_frame - receive frame from NCI drivers
131862306a36Sopenharmony_ci *
131962306a36Sopenharmony_ci * @ndev: The nci device
132062306a36Sopenharmony_ci * @skb: The sk_buff to receive
132162306a36Sopenharmony_ci */
132262306a36Sopenharmony_ciint nci_recv_frame(struct nci_dev *ndev, struct sk_buff *skb)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	pr_debug("len %d\n", skb->len);
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	if (!ndev || (!test_bit(NCI_UP, &ndev->flags) &&
132762306a36Sopenharmony_ci	    !test_bit(NCI_INIT, &ndev->flags))) {
132862306a36Sopenharmony_ci		kfree_skb(skb);
132962306a36Sopenharmony_ci		return -ENXIO;
133062306a36Sopenharmony_ci	}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	/* Queue frame for rx worker thread */
133362306a36Sopenharmony_ci	skb_queue_tail(&ndev->rx_q, skb);
133462306a36Sopenharmony_ci	queue_work(ndev->rx_wq, &ndev->rx_work);
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	return 0;
133762306a36Sopenharmony_ci}
133862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_recv_frame);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ciint nci_send_frame(struct nci_dev *ndev, struct sk_buff *skb)
134162306a36Sopenharmony_ci{
134262306a36Sopenharmony_ci	pr_debug("len %d\n", skb->len);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	if (!ndev) {
134562306a36Sopenharmony_ci		kfree_skb(skb);
134662306a36Sopenharmony_ci		return -ENODEV;
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	/* Get rid of skb owner, prior to sending to the driver. */
135062306a36Sopenharmony_ci	skb_orphan(skb);
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* Send copy to sniffer */
135362306a36Sopenharmony_ci	nfc_send_to_raw_sock(ndev->nfc_dev, skb,
135462306a36Sopenharmony_ci			     RAW_PAYLOAD_NCI, NFC_DIRECTION_TX);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	return ndev->ops->send(ndev, skb);
135762306a36Sopenharmony_ci}
135862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_send_frame);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci/* Send NCI command */
136162306a36Sopenharmony_ciint nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, const void *payload)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct nci_ctrl_hdr *hdr;
136462306a36Sopenharmony_ci	struct sk_buff *skb;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	pr_debug("opcode 0x%x, plen %d\n", opcode, plen);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci	skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL);
136962306a36Sopenharmony_ci	if (!skb) {
137062306a36Sopenharmony_ci		pr_err("no memory for command\n");
137162306a36Sopenharmony_ci		return -ENOMEM;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	hdr = skb_put(skb, NCI_CTRL_HDR_SIZE);
137562306a36Sopenharmony_ci	hdr->gid = nci_opcode_gid(opcode);
137662306a36Sopenharmony_ci	hdr->oid = nci_opcode_oid(opcode);
137762306a36Sopenharmony_ci	hdr->plen = plen;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT);
138062306a36Sopenharmony_ci	nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if (plen)
138362306a36Sopenharmony_ci		skb_put_data(skb, payload, plen);
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci	skb_queue_tail(&ndev->cmd_q, skb);
138662306a36Sopenharmony_ci	queue_work(ndev->cmd_wq, &ndev->cmd_work);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	return 0;
138962306a36Sopenharmony_ci}
139062306a36Sopenharmony_ciEXPORT_SYMBOL(nci_send_cmd);
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci/* Proprietary commands API */
139362306a36Sopenharmony_cistatic const struct nci_driver_ops *ops_cmd_lookup(const struct nci_driver_ops *ops,
139462306a36Sopenharmony_ci						   size_t n_ops,
139562306a36Sopenharmony_ci						   __u16 opcode)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	size_t i;
139862306a36Sopenharmony_ci	const struct nci_driver_ops *op;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (!ops || !n_ops)
140162306a36Sopenharmony_ci		return NULL;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	for (i = 0; i < n_ops; i++) {
140462306a36Sopenharmony_ci		op = &ops[i];
140562306a36Sopenharmony_ci		if (op->opcode == opcode)
140662306a36Sopenharmony_ci			return op;
140762306a36Sopenharmony_ci	}
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	return NULL;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic int nci_op_rsp_packet(struct nci_dev *ndev, __u16 rsp_opcode,
141362306a36Sopenharmony_ci			     struct sk_buff *skb, const struct nci_driver_ops *ops,
141462306a36Sopenharmony_ci			     size_t n_ops)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	const struct nci_driver_ops *op;
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	op = ops_cmd_lookup(ops, n_ops, rsp_opcode);
141962306a36Sopenharmony_ci	if (!op || !op->rsp)
142062306a36Sopenharmony_ci		return -ENOTSUPP;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	return op->rsp(ndev, skb);
142362306a36Sopenharmony_ci}
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_cistatic int nci_op_ntf_packet(struct nci_dev *ndev, __u16 ntf_opcode,
142662306a36Sopenharmony_ci			     struct sk_buff *skb, const struct nci_driver_ops *ops,
142762306a36Sopenharmony_ci			     size_t n_ops)
142862306a36Sopenharmony_ci{
142962306a36Sopenharmony_ci	const struct nci_driver_ops *op;
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci	op = ops_cmd_lookup(ops, n_ops, ntf_opcode);
143262306a36Sopenharmony_ci	if (!op || !op->ntf)
143362306a36Sopenharmony_ci		return -ENOTSUPP;
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	return op->ntf(ndev, skb);
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ciint nci_prop_rsp_packet(struct nci_dev *ndev, __u16 opcode,
143962306a36Sopenharmony_ci			struct sk_buff *skb)
144062306a36Sopenharmony_ci{
144162306a36Sopenharmony_ci	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->prop_ops,
144262306a36Sopenharmony_ci				 ndev->ops->n_prop_ops);
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ciint nci_prop_ntf_packet(struct nci_dev *ndev, __u16 opcode,
144662306a36Sopenharmony_ci			struct sk_buff *skb)
144762306a36Sopenharmony_ci{
144862306a36Sopenharmony_ci	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->prop_ops,
144962306a36Sopenharmony_ci				 ndev->ops->n_prop_ops);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ciint nci_core_rsp_packet(struct nci_dev *ndev, __u16 opcode,
145362306a36Sopenharmony_ci			struct sk_buff *skb)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	return nci_op_rsp_packet(ndev, opcode, skb, ndev->ops->core_ops,
145662306a36Sopenharmony_ci				  ndev->ops->n_core_ops);
145762306a36Sopenharmony_ci}
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ciint nci_core_ntf_packet(struct nci_dev *ndev, __u16 opcode,
146062306a36Sopenharmony_ci			struct sk_buff *skb)
146162306a36Sopenharmony_ci{
146262306a36Sopenharmony_ci	return nci_op_ntf_packet(ndev, opcode, skb, ndev->ops->core_ops,
146362306a36Sopenharmony_ci				 ndev->ops->n_core_ops);
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci/* ---- NCI TX Data worker thread ---- */
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_cistatic void nci_tx_work(struct work_struct *work)
146962306a36Sopenharmony_ci{
147062306a36Sopenharmony_ci	struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work);
147162306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
147262306a36Sopenharmony_ci	struct sk_buff *skb;
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	conn_info = nci_get_conn_info_by_conn_id(ndev, ndev->cur_conn_id);
147562306a36Sopenharmony_ci	if (!conn_info)
147662306a36Sopenharmony_ci		return;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	pr_debug("credits_cnt %d\n", atomic_read(&conn_info->credits_cnt));
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	/* Send queued tx data */
148162306a36Sopenharmony_ci	while (atomic_read(&conn_info->credits_cnt)) {
148262306a36Sopenharmony_ci		skb = skb_dequeue(&ndev->tx_q);
148362306a36Sopenharmony_ci		if (!skb)
148462306a36Sopenharmony_ci			return;
148562306a36Sopenharmony_ci		kcov_remote_start_common(skb_get_kcov_handle(skb));
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci		/* Check if data flow control is used */
148862306a36Sopenharmony_ci		if (atomic_read(&conn_info->credits_cnt) !=
148962306a36Sopenharmony_ci		    NCI_DATA_FLOW_CONTROL_NOT_USED)
149062306a36Sopenharmony_ci			atomic_dec(&conn_info->credits_cnt);
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci		pr_debug("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d\n",
149362306a36Sopenharmony_ci			 nci_pbf(skb->data),
149462306a36Sopenharmony_ci			 nci_conn_id(skb->data),
149562306a36Sopenharmony_ci			 nci_plen(skb->data));
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci		nci_send_frame(ndev, skb);
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci		mod_timer(&ndev->data_timer,
150062306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(NCI_DATA_TIMEOUT));
150162306a36Sopenharmony_ci		kcov_remote_stop();
150262306a36Sopenharmony_ci	}
150362306a36Sopenharmony_ci}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci/* ----- NCI RX worker thread (data & control) ----- */
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic void nci_rx_work(struct work_struct *work)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work);
151062306a36Sopenharmony_ci	struct sk_buff *skb;
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	for (; (skb = skb_dequeue(&ndev->rx_q)); kcov_remote_stop()) {
151362306a36Sopenharmony_ci		kcov_remote_start_common(skb_get_kcov_handle(skb));
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci		/* Send copy to sniffer */
151662306a36Sopenharmony_ci		nfc_send_to_raw_sock(ndev->nfc_dev, skb,
151762306a36Sopenharmony_ci				     RAW_PAYLOAD_NCI, NFC_DIRECTION_RX);
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci		/* Process frame */
152062306a36Sopenharmony_ci		switch (nci_mt(skb->data)) {
152162306a36Sopenharmony_ci		case NCI_MT_RSP_PKT:
152262306a36Sopenharmony_ci			nci_rsp_packet(ndev, skb);
152362306a36Sopenharmony_ci			break;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci		case NCI_MT_NTF_PKT:
152662306a36Sopenharmony_ci			nci_ntf_packet(ndev, skb);
152762306a36Sopenharmony_ci			break;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci		case NCI_MT_DATA_PKT:
153062306a36Sopenharmony_ci			nci_rx_data_packet(ndev, skb);
153162306a36Sopenharmony_ci			break;
153262306a36Sopenharmony_ci
153362306a36Sopenharmony_ci		default:
153462306a36Sopenharmony_ci			pr_err("unknown MT 0x%x\n", nci_mt(skb->data));
153562306a36Sopenharmony_ci			kfree_skb(skb);
153662306a36Sopenharmony_ci			break;
153762306a36Sopenharmony_ci		}
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/* check if a data exchange timeout has occurred */
154162306a36Sopenharmony_ci	if (test_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags)) {
154262306a36Sopenharmony_ci		/* complete the data exchange transaction, if exists */
154362306a36Sopenharmony_ci		if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
154462306a36Sopenharmony_ci			nci_data_exchange_complete(ndev, NULL,
154562306a36Sopenharmony_ci						   ndev->cur_conn_id,
154662306a36Sopenharmony_ci						   -ETIMEDOUT);
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci		clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags);
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci/* ----- NCI TX CMD worker thread ----- */
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic void nci_cmd_work(struct work_struct *work)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work);
155762306a36Sopenharmony_ci	struct sk_buff *skb;
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ci	pr_debug("cmd_cnt %d\n", atomic_read(&ndev->cmd_cnt));
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	/* Send queued command */
156262306a36Sopenharmony_ci	if (atomic_read(&ndev->cmd_cnt)) {
156362306a36Sopenharmony_ci		skb = skb_dequeue(&ndev->cmd_q);
156462306a36Sopenharmony_ci		if (!skb)
156562306a36Sopenharmony_ci			return;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci		kcov_remote_start_common(skb_get_kcov_handle(skb));
156862306a36Sopenharmony_ci		atomic_dec(&ndev->cmd_cnt);
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		pr_debug("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d\n",
157162306a36Sopenharmony_ci			 nci_pbf(skb->data),
157262306a36Sopenharmony_ci			 nci_opcode_gid(nci_opcode(skb->data)),
157362306a36Sopenharmony_ci			 nci_opcode_oid(nci_opcode(skb->data)),
157462306a36Sopenharmony_ci			 nci_plen(skb->data));
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci		nci_send_frame(ndev, skb);
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci		mod_timer(&ndev->cmd_timer,
157962306a36Sopenharmony_ci			  jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT));
158062306a36Sopenharmony_ci		kcov_remote_stop();
158162306a36Sopenharmony_ci	}
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1585