xref: /openbmc/linux/net/nfc/hci/command.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28b8d2e08SEric Lapuyade /*
38b8d2e08SEric Lapuyade  * Copyright (C) 2012  Intel Corporation. All rights reserved.
48b8d2e08SEric Lapuyade  */
58b8d2e08SEric Lapuyade 
68b8d2e08SEric Lapuyade #define pr_fmt(fmt) "hci: %s: " fmt, __func__
78b8d2e08SEric Lapuyade 
88b8d2e08SEric Lapuyade #include <linux/init.h>
98b8d2e08SEric Lapuyade #include <linux/kernel.h>
108b8d2e08SEric Lapuyade #include <linux/sched.h>
118b8d2e08SEric Lapuyade #include <linux/module.h>
128b8d2e08SEric Lapuyade 
138b8d2e08SEric Lapuyade #include <net/nfc/hci.h>
148b8d2e08SEric Lapuyade 
158b8d2e08SEric Lapuyade #include "hci.h"
168b8d2e08SEric Lapuyade 
17d330905dSChristophe Ricard #define MAX_FWI 4949
18d330905dSChristophe Ricard 
nfc_hci_execute_cmd_async(struct nfc_hci_dev * hdev,u8 pipe,u8 cmd,const u8 * param,size_t param_len,data_exchange_cb_t cb,void * cb_context)19e4c4789eSEric Lapuyade static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
20e4c4789eSEric Lapuyade 			       const u8 *param, size_t param_len,
21e4c4789eSEric Lapuyade 			       data_exchange_cb_t cb, void *cb_context)
22e4c4789eSEric Lapuyade {
23e4c4789eSEric Lapuyade 	pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n", pipe,
24e4c4789eSEric Lapuyade 		 cmd, param_len);
25e4c4789eSEric Lapuyade 
26e4c4789eSEric Lapuyade 	/* TODO: Define hci cmd execution delay. Should it be the same
27e4c4789eSEric Lapuyade 	 * for all commands?
28e4c4789eSEric Lapuyade 	 */
29e4c4789eSEric Lapuyade 	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, cmd,
30d330905dSChristophe Ricard 				      param, param_len, cb, cb_context, MAX_FWI);
31e4c4789eSEric Lapuyade }
32e4c4789eSEric Lapuyade 
33b5faa648SEric Lapuyade /*
34b5faa648SEric Lapuyade  * HCI command execution completion callback.
35b5faa648SEric Lapuyade  * err will be a standard linux error (may be converted from HCI response)
36b5faa648SEric Lapuyade  * skb contains the response data and must be disposed, or may be NULL if
37*01709d09SZheng Yongjun  * an error occurred
38b5faa648SEric Lapuyade  */
nfc_hci_execute_cb(void * context,struct sk_buff * skb,int err)39b5faa648SEric Lapuyade static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err)
408b8d2e08SEric Lapuyade {
41b5faa648SEric Lapuyade 	struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context;
428b8d2e08SEric Lapuyade 
436c1c5b9eSEric Lapuyade 	pr_debug("HCI Cmd completed with result=%d\n", err);
448b8d2e08SEric Lapuyade 
456c1c5b9eSEric Lapuyade 	hcp_ew->exec_result = err;
468b8d2e08SEric Lapuyade 	if (hcp_ew->exec_result == 0)
478b8d2e08SEric Lapuyade 		hcp_ew->result_skb = skb;
488b8d2e08SEric Lapuyade 	else
498b8d2e08SEric Lapuyade 		kfree_skb(skb);
508b8d2e08SEric Lapuyade 	hcp_ew->exec_complete = true;
518b8d2e08SEric Lapuyade 
528b8d2e08SEric Lapuyade 	wake_up(hcp_ew->wq);
538b8d2e08SEric Lapuyade }
548b8d2e08SEric Lapuyade 
nfc_hci_execute_cmd(struct nfc_hci_dev * hdev,u8 pipe,u8 cmd,const u8 * param,size_t param_len,struct sk_buff ** skb)558b8d2e08SEric Lapuyade static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
568b8d2e08SEric Lapuyade 			       const u8 *param, size_t param_len,
578b8d2e08SEric Lapuyade 			       struct sk_buff **skb)
588b8d2e08SEric Lapuyade {
598b8d2e08SEric Lapuyade 	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(ew_wq);
608b8d2e08SEric Lapuyade 	struct hcp_exec_waiter hcp_ew;
618b8d2e08SEric Lapuyade 	hcp_ew.wq = &ew_wq;
628b8d2e08SEric Lapuyade 	hcp_ew.exec_complete = false;
638b8d2e08SEric Lapuyade 	hcp_ew.result_skb = NULL;
648b8d2e08SEric Lapuyade 
65e4c4789eSEric Lapuyade 	pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n", pipe,
66e4c4789eSEric Lapuyade 		 cmd, param_len);
678b8d2e08SEric Lapuyade 
688b8d2e08SEric Lapuyade 	/* TODO: Define hci cmd execution delay. Should it be the same
698b8d2e08SEric Lapuyade 	 * for all commands?
708b8d2e08SEric Lapuyade 	 */
718b8d2e08SEric Lapuyade 	hcp_ew.exec_result = nfc_hci_hcp_message_tx(hdev, pipe,
728b8d2e08SEric Lapuyade 						    NFC_HCI_HCP_COMMAND, cmd,
738b8d2e08SEric Lapuyade 						    param, param_len,
748b8d2e08SEric Lapuyade 						    nfc_hci_execute_cb, &hcp_ew,
75d330905dSChristophe Ricard 						    MAX_FWI);
768b8d2e08SEric Lapuyade 	if (hcp_ew.exec_result < 0)
778b8d2e08SEric Lapuyade 		return hcp_ew.exec_result;
788b8d2e08SEric Lapuyade 
798b8d2e08SEric Lapuyade 	wait_event(ew_wq, hcp_ew.exec_complete == true);
808b8d2e08SEric Lapuyade 
818b8d2e08SEric Lapuyade 	if (hcp_ew.exec_result == 0) {
828b8d2e08SEric Lapuyade 		if (skb)
838b8d2e08SEric Lapuyade 			*skb = hcp_ew.result_skb;
848b8d2e08SEric Lapuyade 		else
858b8d2e08SEric Lapuyade 			kfree_skb(hcp_ew.result_skb);
868b8d2e08SEric Lapuyade 	}
878b8d2e08SEric Lapuyade 
888b8d2e08SEric Lapuyade 	return hcp_ew.exec_result;
898b8d2e08SEric Lapuyade }
908b8d2e08SEric Lapuyade 
nfc_hci_send_event(struct nfc_hci_dev * hdev,u8 gate,u8 event,const u8 * param,size_t param_len)918b8d2e08SEric Lapuyade int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event,
928b8d2e08SEric Lapuyade 		       const u8 *param, size_t param_len)
938b8d2e08SEric Lapuyade {
948b8d2e08SEric Lapuyade 	u8 pipe;
958b8d2e08SEric Lapuyade 
968b8d2e08SEric Lapuyade 	pr_debug("%d to gate %d\n", event, gate);
978b8d2e08SEric Lapuyade 
988b8d2e08SEric Lapuyade 	pipe = hdev->gate2pipe[gate];
998b8d2e08SEric Lapuyade 	if (pipe == NFC_HCI_INVALID_PIPE)
1008b8d2e08SEric Lapuyade 		return -EADDRNOTAVAIL;
1018b8d2e08SEric Lapuyade 
1028b8d2e08SEric Lapuyade 	return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_EVENT, event,
1038b8d2e08SEric Lapuyade 				      param, param_len, NULL, NULL, 0);
1048b8d2e08SEric Lapuyade }
1058b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_send_event);
1068b8d2e08SEric Lapuyade 
1078b8d2e08SEric Lapuyade /*
1088b8d2e08SEric Lapuyade  * Execute an hci command sent to gate.
1098b8d2e08SEric Lapuyade  * skb will contain response data if success. skb can be NULL if you are not
1108b8d2e08SEric Lapuyade  * interested by the response.
1118b8d2e08SEric Lapuyade  */
nfc_hci_send_cmd(struct nfc_hci_dev * hdev,u8 gate,u8 cmd,const u8 * param,size_t param_len,struct sk_buff ** skb)1128b8d2e08SEric Lapuyade int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
1138b8d2e08SEric Lapuyade 		     const u8 *param, size_t param_len, struct sk_buff **skb)
1148b8d2e08SEric Lapuyade {
1158b8d2e08SEric Lapuyade 	u8 pipe;
1168b8d2e08SEric Lapuyade 
1178b8d2e08SEric Lapuyade 	pipe = hdev->gate2pipe[gate];
1188b8d2e08SEric Lapuyade 	if (pipe == NFC_HCI_INVALID_PIPE)
1198b8d2e08SEric Lapuyade 		return -EADDRNOTAVAIL;
1208b8d2e08SEric Lapuyade 
1218b8d2e08SEric Lapuyade 	return nfc_hci_execute_cmd(hdev, pipe, cmd, param, param_len, skb);
1228b8d2e08SEric Lapuyade }
1238b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_send_cmd);
1248b8d2e08SEric Lapuyade 
nfc_hci_send_cmd_async(struct nfc_hci_dev * hdev,u8 gate,u8 cmd,const u8 * param,size_t param_len,data_exchange_cb_t cb,void * cb_context)125e4c4789eSEric Lapuyade int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd,
126e4c4789eSEric Lapuyade 			   const u8 *param, size_t param_len,
127e4c4789eSEric Lapuyade 			   data_exchange_cb_t cb, void *cb_context)
128e4c4789eSEric Lapuyade {
129e4c4789eSEric Lapuyade 	u8 pipe;
130e4c4789eSEric Lapuyade 
131e4c4789eSEric Lapuyade 	pipe = hdev->gate2pipe[gate];
132e4c4789eSEric Lapuyade 	if (pipe == NFC_HCI_INVALID_PIPE)
133e4c4789eSEric Lapuyade 		return -EADDRNOTAVAIL;
134e4c4789eSEric Lapuyade 
135e4c4789eSEric Lapuyade 	return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len,
136e4c4789eSEric Lapuyade 					 cb, cb_context);
137e4c4789eSEric Lapuyade }
138e4c4789eSEric Lapuyade EXPORT_SYMBOL(nfc_hci_send_cmd_async);
139e4c4789eSEric Lapuyade 
nfc_hci_set_param(struct nfc_hci_dev * hdev,u8 gate,u8 idx,const u8 * param,size_t param_len)1408b8d2e08SEric Lapuyade int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
1418b8d2e08SEric Lapuyade 		      const u8 *param, size_t param_len)
1428b8d2e08SEric Lapuyade {
1438b8d2e08SEric Lapuyade 	int r;
1448b8d2e08SEric Lapuyade 	u8 *tmp;
1458b8d2e08SEric Lapuyade 
1468b8d2e08SEric Lapuyade 	/* TODO ELa: reg idx must be inserted before param, but we don't want
1478b8d2e08SEric Lapuyade 	 * to ask the caller to do it to keep a simpler API.
1488b8d2e08SEric Lapuyade 	 * For now, just create a new temporary param buffer. This is far from
1498b8d2e08SEric Lapuyade 	 * optimal though, and the plan is to modify APIs to pass idx down to
1508b8d2e08SEric Lapuyade 	 * nfc_hci_hcp_message_tx where the frame is actually built, thereby
1518b8d2e08SEric Lapuyade 	 * eliminating the need for the temp allocation-copy here.
1528b8d2e08SEric Lapuyade 	 */
1538b8d2e08SEric Lapuyade 
1548b8d2e08SEric Lapuyade 	pr_debug("idx=%d to gate %d\n", idx, gate);
1558b8d2e08SEric Lapuyade 
1568b8d2e08SEric Lapuyade 	tmp = kmalloc(1 + param_len, GFP_KERNEL);
1578b8d2e08SEric Lapuyade 	if (tmp == NULL)
1588b8d2e08SEric Lapuyade 		return -ENOMEM;
1598b8d2e08SEric Lapuyade 
1608b8d2e08SEric Lapuyade 	*tmp = idx;
1618b8d2e08SEric Lapuyade 	memcpy(tmp + 1, param, param_len);
1628b8d2e08SEric Lapuyade 
1638b8d2e08SEric Lapuyade 	r = nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_SET_PARAMETER,
1648b8d2e08SEric Lapuyade 			     tmp, param_len + 1, NULL);
1658b8d2e08SEric Lapuyade 
1668b8d2e08SEric Lapuyade 	kfree(tmp);
1678b8d2e08SEric Lapuyade 
1688b8d2e08SEric Lapuyade 	return r;
1698b8d2e08SEric Lapuyade }
1708b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_set_param);
1718b8d2e08SEric Lapuyade 
nfc_hci_get_param(struct nfc_hci_dev * hdev,u8 gate,u8 idx,struct sk_buff ** skb)1728b8d2e08SEric Lapuyade int nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx,
1738b8d2e08SEric Lapuyade 		      struct sk_buff **skb)
1748b8d2e08SEric Lapuyade {
1758b8d2e08SEric Lapuyade 	pr_debug("gate=%d regidx=%d\n", gate, idx);
1768b8d2e08SEric Lapuyade 
1778b8d2e08SEric Lapuyade 	return nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_GET_PARAMETER,
1788b8d2e08SEric Lapuyade 				&idx, 1, skb);
1798b8d2e08SEric Lapuyade }
1808b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_get_param);
1818b8d2e08SEric Lapuyade 
nfc_hci_open_pipe(struct nfc_hci_dev * hdev,u8 pipe)1828b8d2e08SEric Lapuyade static int nfc_hci_open_pipe(struct nfc_hci_dev *hdev, u8 pipe)
1838b8d2e08SEric Lapuyade {
1848b8d2e08SEric Lapuyade 	struct sk_buff *skb;
1858b8d2e08SEric Lapuyade 	int r;
1868b8d2e08SEric Lapuyade 
1878b8d2e08SEric Lapuyade 	pr_debug("pipe=%d\n", pipe);
1888b8d2e08SEric Lapuyade 
1898b8d2e08SEric Lapuyade 	r = nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_OPEN_PIPE,
1908b8d2e08SEric Lapuyade 				NULL, 0, &skb);
1918b8d2e08SEric Lapuyade 	if (r == 0) {
1928b8d2e08SEric Lapuyade 		/* dest host other than host controller will send
1938b8d2e08SEric Lapuyade 		 * number of pipes already open on this gate before
1948b8d2e08SEric Lapuyade 		 * execution. The number can be found in skb->data[0]
1958b8d2e08SEric Lapuyade 		 */
1968b8d2e08SEric Lapuyade 		kfree_skb(skb);
1978b8d2e08SEric Lapuyade 	}
1988b8d2e08SEric Lapuyade 
1998b8d2e08SEric Lapuyade 	return r;
2008b8d2e08SEric Lapuyade }
2018b8d2e08SEric Lapuyade 
nfc_hci_close_pipe(struct nfc_hci_dev * hdev,u8 pipe)2028b8d2e08SEric Lapuyade static int nfc_hci_close_pipe(struct nfc_hci_dev *hdev, u8 pipe)
2038b8d2e08SEric Lapuyade {
2048b8d2e08SEric Lapuyade 	return nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_CLOSE_PIPE,
2058b8d2e08SEric Lapuyade 				   NULL, 0, NULL);
2068b8d2e08SEric Lapuyade }
2078b8d2e08SEric Lapuyade 
nfc_hci_create_pipe(struct nfc_hci_dev * hdev,u8 dest_host,u8 dest_gate,int * result)2088b8d2e08SEric Lapuyade static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host,
2098b8d2e08SEric Lapuyade 			      u8 dest_gate, int *result)
2108b8d2e08SEric Lapuyade {
2118b8d2e08SEric Lapuyade 	struct sk_buff *skb;
2128b8d2e08SEric Lapuyade 	struct hci_create_pipe_params params;
2138b8d2e08SEric Lapuyade 	struct hci_create_pipe_resp *resp;
2148b8d2e08SEric Lapuyade 	u8 pipe;
2158b8d2e08SEric Lapuyade 
2168b8d2e08SEric Lapuyade 	pr_debug("gate=%d\n", dest_gate);
2178b8d2e08SEric Lapuyade 
2188b8d2e08SEric Lapuyade 	params.src_gate = NFC_HCI_ADMIN_GATE;
2198b8d2e08SEric Lapuyade 	params.dest_host = dest_host;
2208b8d2e08SEric Lapuyade 	params.dest_gate = dest_gate;
2218b8d2e08SEric Lapuyade 
2228b8d2e08SEric Lapuyade 	*result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
2238b8d2e08SEric Lapuyade 				      NFC_HCI_ADM_CREATE_PIPE,
2248b8d2e08SEric Lapuyade 				      (u8 *) &params, sizeof(params), &skb);
22580e4232eSSzymon Janc 	if (*result < 0)
22680e4232eSSzymon Janc 		return NFC_HCI_INVALID_PIPE;
22780e4232eSSzymon Janc 
2288b8d2e08SEric Lapuyade 	resp = (struct hci_create_pipe_resp *)skb->data;
2298b8d2e08SEric Lapuyade 	pipe = resp->pipe;
2308b8d2e08SEric Lapuyade 	kfree_skb(skb);
2318b8d2e08SEric Lapuyade 
2328b8d2e08SEric Lapuyade 	pr_debug("pipe created=%d\n", pipe);
2338b8d2e08SEric Lapuyade 
2348b8d2e08SEric Lapuyade 	return pipe;
2358b8d2e08SEric Lapuyade }
2368b8d2e08SEric Lapuyade 
nfc_hci_delete_pipe(struct nfc_hci_dev * hdev,u8 pipe)2378b8d2e08SEric Lapuyade static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe)
2388b8d2e08SEric Lapuyade {
2398b8d2e08SEric Lapuyade 	return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
2408b8d2e08SEric Lapuyade 				   NFC_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL);
2418b8d2e08SEric Lapuyade }
2428b8d2e08SEric Lapuyade 
nfc_hci_clear_all_pipes(struct nfc_hci_dev * hdev)2438b8d2e08SEric Lapuyade static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev)
2448b8d2e08SEric Lapuyade {
2458b8d2e08SEric Lapuyade 	u8 param[2];
246bf71ab8bSEric Lapuyade 	size_t param_len = 2;
2478b8d2e08SEric Lapuyade 
2488b8d2e08SEric Lapuyade 	/* TODO: Find out what the identity reference data is
2498b8d2e08SEric Lapuyade 	 * and fill param with it. HCI spec 6.1.3.5 */
2508b8d2e08SEric Lapuyade 
251bf71ab8bSEric Lapuyade 	if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks))
252bf71ab8bSEric Lapuyade 		param_len = 0;
253bf71ab8bSEric Lapuyade 
2540250ffc5SSzymon Janc 	return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE,
255bf71ab8bSEric Lapuyade 				   NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len,
256bf71ab8bSEric Lapuyade 				   NULL);
2578b8d2e08SEric Lapuyade }
2588b8d2e08SEric Lapuyade 
nfc_hci_disconnect_gate(struct nfc_hci_dev * hdev,u8 gate)2598b8d2e08SEric Lapuyade int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate)
2608b8d2e08SEric Lapuyade {
2618b8d2e08SEric Lapuyade 	int r;
2628b8d2e08SEric Lapuyade 	u8 pipe = hdev->gate2pipe[gate];
2638b8d2e08SEric Lapuyade 
2648b8d2e08SEric Lapuyade 	if (pipe == NFC_HCI_INVALID_PIPE)
2658b8d2e08SEric Lapuyade 		return -EADDRNOTAVAIL;
2668b8d2e08SEric Lapuyade 
2678b8d2e08SEric Lapuyade 	r = nfc_hci_close_pipe(hdev, pipe);
2688b8d2e08SEric Lapuyade 	if (r < 0)
2698b8d2e08SEric Lapuyade 		return r;
2708b8d2e08SEric Lapuyade 
2718b8d2e08SEric Lapuyade 	if (pipe != NFC_HCI_LINK_MGMT_PIPE && pipe != NFC_HCI_ADMIN_PIPE) {
2728b8d2e08SEric Lapuyade 		r = nfc_hci_delete_pipe(hdev, pipe);
2738b8d2e08SEric Lapuyade 		if (r < 0)
2748b8d2e08SEric Lapuyade 			return r;
2758b8d2e08SEric Lapuyade 	}
2768b8d2e08SEric Lapuyade 
2778b8d2e08SEric Lapuyade 	hdev->gate2pipe[gate] = NFC_HCI_INVALID_PIPE;
2788b8d2e08SEric Lapuyade 
2798b8d2e08SEric Lapuyade 	return 0;
2808b8d2e08SEric Lapuyade }
2818b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_disconnect_gate);
2828b8d2e08SEric Lapuyade 
nfc_hci_disconnect_all_gates(struct nfc_hci_dev * hdev)2838b8d2e08SEric Lapuyade int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev)
2848b8d2e08SEric Lapuyade {
2858b8d2e08SEric Lapuyade 	int r;
2868b8d2e08SEric Lapuyade 
2878b8d2e08SEric Lapuyade 	r = nfc_hci_clear_all_pipes(hdev);
2888b8d2e08SEric Lapuyade 	if (r < 0)
2898b8d2e08SEric Lapuyade 		return r;
2908b8d2e08SEric Lapuyade 
291118278f2SChristophe Ricard 	nfc_hci_reset_pipes(hdev);
2928b8d2e08SEric Lapuyade 
2938b8d2e08SEric Lapuyade 	return 0;
2948b8d2e08SEric Lapuyade }
2958b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_disconnect_all_gates);
2968b8d2e08SEric Lapuyade 
nfc_hci_connect_gate(struct nfc_hci_dev * hdev,u8 dest_host,u8 dest_gate,u8 pipe)297a10d595bSEric Lapuyade int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
298a10d595bSEric Lapuyade 			 u8 pipe)
2998b8d2e08SEric Lapuyade {
3008b8d2e08SEric Lapuyade 	bool pipe_created = false;
3018b8d2e08SEric Lapuyade 	int r;
3028b8d2e08SEric Lapuyade 
303118278f2SChristophe Ricard 	if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE)
304b3a55b9cSChristophe Ricard 		return 0;
305b3a55b9cSChristophe Ricard 
3068b8d2e08SEric Lapuyade 	if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
3078b8d2e08SEric Lapuyade 		return -EADDRINUSE;
3088b8d2e08SEric Lapuyade 
309a10d595bSEric Lapuyade 	if (pipe != NFC_HCI_INVALID_PIPE)
31007887e92SEric Lapuyade 		goto open_pipe;
311a10d595bSEric Lapuyade 
3128b8d2e08SEric Lapuyade 	switch (dest_gate) {
3138b8d2e08SEric Lapuyade 	case NFC_HCI_LINK_MGMT_GATE:
3148b8d2e08SEric Lapuyade 		pipe = NFC_HCI_LINK_MGMT_PIPE;
3158b8d2e08SEric Lapuyade 		break;
3168b8d2e08SEric Lapuyade 	case NFC_HCI_ADMIN_GATE:
3178b8d2e08SEric Lapuyade 		pipe = NFC_HCI_ADMIN_PIPE;
3188b8d2e08SEric Lapuyade 		break;
3198b8d2e08SEric Lapuyade 	default:
3208b8d2e08SEric Lapuyade 		pipe = nfc_hci_create_pipe(hdev, dest_host, dest_gate, &r);
3218b8d2e08SEric Lapuyade 		if (pipe == NFC_HCI_INVALID_PIPE)
3228b8d2e08SEric Lapuyade 			return r;
3238b8d2e08SEric Lapuyade 		pipe_created = true;
3248b8d2e08SEric Lapuyade 		break;
3258b8d2e08SEric Lapuyade 	}
3268b8d2e08SEric Lapuyade 
32707887e92SEric Lapuyade open_pipe:
3288b8d2e08SEric Lapuyade 	r = nfc_hci_open_pipe(hdev, pipe);
3298b8d2e08SEric Lapuyade 	if (r < 0) {
3308b8d2e08SEric Lapuyade 		if (pipe_created)
3318b8d2e08SEric Lapuyade 			if (nfc_hci_delete_pipe(hdev, pipe) < 0) {
3328b8d2e08SEric Lapuyade 				/* TODO: Cannot clean by deleting pipe...
3338b8d2e08SEric Lapuyade 				 * -> inconsistent state */
3348b8d2e08SEric Lapuyade 			}
3358b8d2e08SEric Lapuyade 		return r;
3368b8d2e08SEric Lapuyade 	}
3378b8d2e08SEric Lapuyade 
338118278f2SChristophe Ricard 	hdev->pipes[pipe].gate = dest_gate;
339118278f2SChristophe Ricard 	hdev->pipes[pipe].dest_host = dest_host;
3408b8d2e08SEric Lapuyade 	hdev->gate2pipe[dest_gate] = pipe;
3418b8d2e08SEric Lapuyade 
3428b8d2e08SEric Lapuyade 	return 0;
3438b8d2e08SEric Lapuyade }
3448b8d2e08SEric Lapuyade EXPORT_SYMBOL(nfc_hci_connect_gate);
345