xref: /kernel/linux/linux-5.10/net/nfc/hci/command.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012  Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hci: %s: " fmt, __func__
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <net/nfc/hci.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "hci.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define MAX_FWI 4949
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
208c2ecf20Sopenharmony_ci			       const u8 *param, size_t param_len,
218c2ecf20Sopenharmony_ci			       data_exchange_cb_t cb, void *cb_context)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
248c2ecf20Sopenharmony_ci		 cmd, param_len);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* TODO: Define hci cmd execution delay. Should it be the same
278c2ecf20Sopenharmony_ci	 * for all commands?
288c2ecf20Sopenharmony_ci	 */
298c2ecf20Sopenharmony_ci	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
308c2ecf20Sopenharmony_ci				      param, param_len, cb, cb_context, MAX_FWI);
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * HCI command execution completion callback.
358c2ecf20Sopenharmony_ci * err will be a standard linux error (may be converted from HCI response)
368c2ecf20Sopenharmony_ci * skb contains the response data and must be disposed, or may be NULL if
378c2ecf20Sopenharmony_ci * an error occured
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	pr_debug("HCI Cmd completed with result=%d\n", err);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	hcp_ew->exec_result = err;
468c2ecf20Sopenharmony_ci	if (hcp_ew->exec_result == 0)
478c2ecf20Sopenharmony_ci		hcp_ew->result_skb = skb;
488c2ecf20Sopenharmony_ci	else
498c2ecf20Sopenharmony_ci		kfree_skb(skb);
508c2ecf20Sopenharmony_ci	hcp_ew->exec_complete = true;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	wake_up(hcp_ew->wq);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
568c2ecf20Sopenharmony_ci			       const u8 *param, size_t param_len,
578c2ecf20Sopenharmony_ci			       struct sk_buff **skb)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(ew_wq);
608c2ecf20Sopenharmony_ci	struct hcp_exec_waiter hcp_ew;
618c2ecf20Sopenharmony_ci	hcp_ew.wq = &ew_wq;
628c2ecf20Sopenharmony_ci	hcp_ew.exec_complete = false;
638c2ecf20Sopenharmony_ci	hcp_ew.result_skb = NULL;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
668c2ecf20Sopenharmony_ci		 cmd, param_len);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/* TODO: Define hci cmd execution delay. Should it be the same
698c2ecf20Sopenharmony_ci	 * for all commands?
708c2ecf20Sopenharmony_ci	 */
718c2ecf20Sopenharmony_ci	hcp_ew.exec_result = nfc_hci_hcp_message_tx(hdev, pipe,
728c2ecf20Sopenharmony_ci						    NFC_HCI_HCP_COMMAND, cmd,
738c2ecf20Sopenharmony_ci						    param, param_len,
748c2ecf20Sopenharmony_ci						    nfc_hci_execute_cb, &hcp_ew,
758c2ecf20Sopenharmony_ci						    MAX_FWI);
768c2ecf20Sopenharmony_ci	if (hcp_ew.exec_result < 0)
778c2ecf20Sopenharmony_ci		return hcp_ew.exec_result;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	wait_event(ew_wq, hcp_ew.exec_complete == true);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (hcp_ew.exec_result == 0) {
828c2ecf20Sopenharmony_ci		if (skb)
838c2ecf20Sopenharmony_ci			*skb = hcp_ew.result_skb;
848c2ecf20Sopenharmony_ci		else
858c2ecf20Sopenharmony_ci			kfree_skb(hcp_ew.result_skb);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return hcp_ew.exec_result;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciint nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
928c2ecf20Sopenharmony_ci		       const u8 *param, size_t param_len)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u8 pipe;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	pr_debug("%d to gate %d\n", event, gate);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	pipe = hdev->gate2pipe[gate];
998c2ecf20Sopenharmony_ci	if (pipe == NFC_HCI_INVALID_PIPE)
1008c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_EVENT, event,
1038c2ecf20Sopenharmony_ci				      param, param_len, NULL, NULL, 0);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_event);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/*
1088c2ecf20Sopenharmony_ci * Execute an hci command sent to gate.
1098c2ecf20Sopenharmony_ci * skb will contain response data if success. skb can be NULL if you are not
1108c2ecf20Sopenharmony_ci * interested by the response.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_ciint nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
1138c2ecf20Sopenharmony_ci		     const u8 *param, size_t param_len, struct sk_buff **skb)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	u8 pipe;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	pr_debug("\n");
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	pipe = hdev->gate2pipe[gate];
1208c2ecf20Sopenharmony_ci	if (pipe == NFC_HCI_INVALID_PIPE)
1218c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return nfc_hci_execute_cmd(hdev, pipe, cmd, param, param_len, skb);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_cmd);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciint nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
1288c2ecf20Sopenharmony_ci			   const u8 *param, size_t param_len,
1298c2ecf20Sopenharmony_ci			   data_exchange_cb_t cb, void *cb_context)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	u8 pipe;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	pr_debug("\n");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	pipe = hdev->gate2pipe[gate];
1368c2ecf20Sopenharmony_ci	if (pipe == NFC_HCI_INVALID_PIPE)
1378c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
1408c2ecf20Sopenharmony_ci					 cb, cb_context);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_send_cmd_async);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ciint nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
1458c2ecf20Sopenharmony_ci		      const u8 *param, size_t param_len)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int r;
1488c2ecf20Sopenharmony_ci	u8 *tmp;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* TODO ELa: reg idx must be inserted before param, but we don't want
1518c2ecf20Sopenharmony_ci	 * to ask the caller to do it to keep a simpler API.
1528c2ecf20Sopenharmony_ci	 * For now, just create a new temporary param buffer. This is far from
1538c2ecf20Sopenharmony_ci	 * optimal though, and the plan is to modify APIs to pass idx down to
1548c2ecf20Sopenharmony_ci	 * nfc_hci_hcp_message_tx where the frame is actually built, thereby
1558c2ecf20Sopenharmony_ci	 * eliminating the need for the temp allocation-copy here.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	pr_debug("idx=%d to gate %d\n", idx, gate);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	tmp = kmalloc(1 + param_len, GFP_KERNEL);
1618c2ecf20Sopenharmony_ci	if (tmp == NULL)
1628c2ecf20Sopenharmony_ci		return -ENOMEM;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	*tmp = idx;
1658c2ecf20Sopenharmony_ci	memcpy(tmp + 1, param, param_len);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	r = nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_SET_PARAMETER,
1688c2ecf20Sopenharmony_ci			     tmp, param_len + 1, NULL);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	kfree(tmp);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return r;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_set_param);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciint nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
1778c2ecf20Sopenharmony_ci		      struct sk_buff **skb)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	pr_debug("gate=%d regidx=%d\n", gate, idx);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_GET_PARAMETER,
1828c2ecf20Sopenharmony_ci				&idx, 1, skb);
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_get_param);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int nfc_hci_open_pipe(struct nfc_hci_dev *hdev, u8 pipe)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct sk_buff *skb;
1898c2ecf20Sopenharmony_ci	int r;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	pr_debug("pipe=%d\n", pipe);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	r = nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_OPEN_PIPE,
1948c2ecf20Sopenharmony_ci				NULL, 0, &skb);
1958c2ecf20Sopenharmony_ci	if (r == 0) {
1968c2ecf20Sopenharmony_ci		/* dest host other than host controller will send
1978c2ecf20Sopenharmony_ci		 * number of pipes already open on this gate before
1988c2ecf20Sopenharmony_ci		 * execution. The number can be found in skb->data[0]
1998c2ecf20Sopenharmony_ci		 */
2008c2ecf20Sopenharmony_ci		kfree_skb(skb);
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return r;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int nfc_hci_close_pipe(struct nfc_hci_dev *hdev, u8 pipe)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	pr_debug("\n");
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	return nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_CLOSE_PIPE,
2118c2ecf20Sopenharmony_ci				   NULL, 0, NULL);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
2158c2ecf20Sopenharmony_ci			      u8 dest_gate, int *result)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	struct sk_buff *skb;
2188c2ecf20Sopenharmony_ci	struct hci_create_pipe_params params;
2198c2ecf20Sopenharmony_ci	struct hci_create_pipe_resp *resp;
2208c2ecf20Sopenharmony_ci	u8 pipe;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	pr_debug("gate=%d\n", dest_gate);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	params.src_gate = NFC_HCI_ADMIN_GATE;
2258c2ecf20Sopenharmony_ci	params.dest_host = dest_host;
2268c2ecf20Sopenharmony_ci	params.dest_gate = dest_gate;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	*result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
2298c2ecf20Sopenharmony_ci				      NFC_HCI_ADM_CREATE_PIPE,
2308c2ecf20Sopenharmony_ci				      (u8 *) &params, sizeof(params), &skb);
2318c2ecf20Sopenharmony_ci	if (*result < 0)
2328c2ecf20Sopenharmony_ci		return NFC_HCI_INVALID_PIPE;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	resp = (struct hci_create_pipe_resp *)skb->data;
2358c2ecf20Sopenharmony_ci	pipe = resp->pipe;
2368c2ecf20Sopenharmony_ci	kfree_skb(skb);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	pr_debug("pipe created=%d\n", pipe);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	return pipe;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	pr_debug("\n");
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
2488c2ecf20Sopenharmony_ci				   NFC_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	u8 param[2];
2548c2ecf20Sopenharmony_ci	size_t param_len = 2;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	/* TODO: Find out what the identity reference data is
2578c2ecf20Sopenharmony_ci	 * and fill param with it. HCI spec 6.1.3.5 */
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	pr_debug("\n");
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
2628c2ecf20Sopenharmony_ci		param_len = 0;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
2658c2ecf20Sopenharmony_ci				   NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
2668c2ecf20Sopenharmony_ci				   NULL);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ciint nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	int r;
2728c2ecf20Sopenharmony_ci	u8 pipe = hdev->gate2pipe[gate];
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	pr_debug("\n");
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	if (pipe == NFC_HCI_INVALID_PIPE)
2778c2ecf20Sopenharmony_ci		return -EADDRNOTAVAIL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	r = nfc_hci_close_pipe(hdev, pipe);
2808c2ecf20Sopenharmony_ci	if (r < 0)
2818c2ecf20Sopenharmony_ci		return r;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (pipe != NFC_HCI_LINK_MGMT_PIPE && pipe != NFC_HCI_ADMIN_PIPE) {
2848c2ecf20Sopenharmony_ci		r = nfc_hci_delete_pipe(hdev, pipe);
2858c2ecf20Sopenharmony_ci		if (r < 0)
2868c2ecf20Sopenharmony_ci			return r;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	hdev->gate2pipe[gate] = NFC_HCI_INVALID_PIPE;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	return 0;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_disconnect_gate);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ciint nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int r;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	pr_debug("\n");
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	r = nfc_hci_clear_all_pipes(hdev);
3028c2ecf20Sopenharmony_ci	if (r < 0)
3038c2ecf20Sopenharmony_ci		return r;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	nfc_hci_reset_pipes(hdev);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	return 0;
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_disconnect_all_gates);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ciint nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
3128c2ecf20Sopenharmony_ci			 u8 pipe)
3138c2ecf20Sopenharmony_ci{
3148c2ecf20Sopenharmony_ci	bool pipe_created = false;
3158c2ecf20Sopenharmony_ci	int r;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	pr_debug("\n");
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE)
3208c2ecf20Sopenharmony_ci		return 0;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
3238c2ecf20Sopenharmony_ci		return -EADDRINUSE;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (pipe != NFC_HCI_INVALID_PIPE)
3268c2ecf20Sopenharmony_ci		goto open_pipe;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	switch (dest_gate) {
3298c2ecf20Sopenharmony_ci	case NFC_HCI_LINK_MGMT_GATE:
3308c2ecf20Sopenharmony_ci		pipe = NFC_HCI_LINK_MGMT_PIPE;
3318c2ecf20Sopenharmony_ci		break;
3328c2ecf20Sopenharmony_ci	case NFC_HCI_ADMIN_GATE:
3338c2ecf20Sopenharmony_ci		pipe = NFC_HCI_ADMIN_PIPE;
3348c2ecf20Sopenharmony_ci		break;
3358c2ecf20Sopenharmony_ci	default:
3368c2ecf20Sopenharmony_ci		pipe = nfc_hci_create_pipe(hdev, dest_host, dest_gate, &r);
3378c2ecf20Sopenharmony_ci		if (pipe == NFC_HCI_INVALID_PIPE)
3388c2ecf20Sopenharmony_ci			return r;
3398c2ecf20Sopenharmony_ci		pipe_created = true;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ciopen_pipe:
3448c2ecf20Sopenharmony_ci	r = nfc_hci_open_pipe(hdev, pipe);
3458c2ecf20Sopenharmony_ci	if (r < 0) {
3468c2ecf20Sopenharmony_ci		if (pipe_created)
3478c2ecf20Sopenharmony_ci			if (nfc_hci_delete_pipe(hdev, pipe) < 0) {
3488c2ecf20Sopenharmony_ci				/* TODO: Cannot clean by deleting pipe...
3498c2ecf20Sopenharmony_ci				 * -> inconsistent state */
3508c2ecf20Sopenharmony_ci			}
3518c2ecf20Sopenharmony_ci		return r;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	hdev->pipes[pipe].gate = dest_gate;
3558c2ecf20Sopenharmony_ci	hdev->pipes[pipe].dest_host = dest_host;
3568c2ecf20Sopenharmony_ci	hdev->gate2pipe[dest_gate] = pipe;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	return 0;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(nfc_hci_connect_gate);
361