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 *  This is the HCI over NCI implementation, as specified in the 10.2
662306a36Sopenharmony_ci *  section of the NCI 1.1 specification.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  Copyright (C) 2014  STMicroelectronics SAS. All rights reserved.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/skbuff.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "../nfc.h"
1462306a36Sopenharmony_ci#include <net/nfc/nci.h>
1562306a36Sopenharmony_ci#include <net/nfc/nci_core.h>
1662306a36Sopenharmony_ci#include <linux/nfc.h>
1762306a36Sopenharmony_ci#include <linux/kcov.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct nci_data {
2062306a36Sopenharmony_ci	u8 conn_id;
2162306a36Sopenharmony_ci	u8 pipe;
2262306a36Sopenharmony_ci	u8 cmd;
2362306a36Sopenharmony_ci	const u8 *data;
2462306a36Sopenharmony_ci	u32 data_len;
2562306a36Sopenharmony_ci} __packed;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistruct nci_hci_create_pipe_params {
2862306a36Sopenharmony_ci	u8 src_gate;
2962306a36Sopenharmony_ci	u8 dest_host;
3062306a36Sopenharmony_ci	u8 dest_gate;
3162306a36Sopenharmony_ci} __packed;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct nci_hci_create_pipe_resp {
3462306a36Sopenharmony_ci	u8 src_host;
3562306a36Sopenharmony_ci	u8 src_gate;
3662306a36Sopenharmony_ci	u8 dest_host;
3762306a36Sopenharmony_ci	u8 dest_gate;
3862306a36Sopenharmony_ci	u8 pipe;
3962306a36Sopenharmony_ci} __packed;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct nci_hci_delete_pipe_noti {
4262306a36Sopenharmony_ci	u8 pipe;
4362306a36Sopenharmony_ci} __packed;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct nci_hci_all_pipe_cleared_noti {
4662306a36Sopenharmony_ci	u8 host;
4762306a36Sopenharmony_ci} __packed;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct nci_hcp_message {
5062306a36Sopenharmony_ci	u8 header;      /* type -cmd,evt,rsp- + instruction */
5162306a36Sopenharmony_ci	u8 data[];
5262306a36Sopenharmony_ci} __packed;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct nci_hcp_packet {
5562306a36Sopenharmony_ci	u8 header;      /* cbit+pipe */
5662306a36Sopenharmony_ci	struct nci_hcp_message message;
5762306a36Sopenharmony_ci} __packed;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define NCI_HCI_ANY_SET_PARAMETER  0x01
6062306a36Sopenharmony_ci#define NCI_HCI_ANY_GET_PARAMETER  0x02
6162306a36Sopenharmony_ci#define NCI_HCI_ANY_CLOSE_PIPE     0x04
6262306a36Sopenharmony_ci#define NCI_HCI_ADM_CLEAR_ALL_PIPE 0x14
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define NCI_HFP_NO_CHAINING        0x80
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define NCI_NFCEE_ID_HCI                0x80
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define NCI_EVT_HOT_PLUG           0x03
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
7162306a36Sopenharmony_ci#define NCI_HCI_ADM_CREATE_PIPE			0x10
7262306a36Sopenharmony_ci#define NCI_HCI_ADM_DELETE_PIPE			0x11
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* HCP headers */
7562306a36Sopenharmony_ci#define NCI_HCI_HCP_PACKET_HEADER_LEN      1
7662306a36Sopenharmony_ci#define NCI_HCI_HCP_MESSAGE_HEADER_LEN     1
7762306a36Sopenharmony_ci#define NCI_HCI_HCP_HEADER_LEN             2
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* HCP types */
8062306a36Sopenharmony_ci#define NCI_HCI_HCP_COMMAND        0x00
8162306a36Sopenharmony_ci#define NCI_HCI_HCP_EVENT          0x01
8262306a36Sopenharmony_ci#define NCI_HCI_HCP_RESPONSE       0x02
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_CREATED     0x12
8562306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_PIPE_DELETED     0x13
8662306a36Sopenharmony_ci#define NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED 0x15
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci#define NCI_HCI_FRAGMENT           0x7f
8962306a36Sopenharmony_ci#define NCI_HCP_HEADER(type, instr) ((((type) & 0x03) << 6) |\
9062306a36Sopenharmony_ci				      ((instr) & 0x3f))
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_TYPE(header) ((header & 0xc0) >> 6)
9362306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_CMD(header)  (header & 0x3f)
9462306a36Sopenharmony_ci#define NCI_HCP_MSG_GET_PIPE(header) (header & 0x7f)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int nci_hci_result_to_errno(u8 result)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	switch (result) {
9962306a36Sopenharmony_ci	case NCI_HCI_ANY_OK:
10062306a36Sopenharmony_ci		return 0;
10162306a36Sopenharmony_ci	case NCI_HCI_ANY_E_REG_PAR_UNKNOWN:
10262306a36Sopenharmony_ci		return -EOPNOTSUPP;
10362306a36Sopenharmony_ci	case NCI_HCI_ANY_E_TIMEOUT:
10462306a36Sopenharmony_ci		return -ETIME;
10562306a36Sopenharmony_ci	default:
10662306a36Sopenharmony_ci		return -1;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/* HCI core */
11162306a36Sopenharmony_cistatic void nci_hci_reset_pipes(struct nci_hci_dev *hdev)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	int i;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	for (i = 0; i < NCI_HCI_MAX_PIPES; i++) {
11662306a36Sopenharmony_ci		hdev->pipes[i].gate = NCI_HCI_INVALID_GATE;
11762306a36Sopenharmony_ci		hdev->pipes[i].host = NCI_HCI_INVALID_HOST;
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	memset(hdev->gate2pipe, NCI_HCI_INVALID_PIPE, sizeof(hdev->gate2pipe));
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void nci_hci_reset_pipes_per_host(struct nci_dev *ndev, u8 host)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	int i;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	for (i = 0; i < NCI_HCI_MAX_PIPES; i++) {
12762306a36Sopenharmony_ci		if (ndev->hci_dev->pipes[i].host == host) {
12862306a36Sopenharmony_ci			ndev->hci_dev->pipes[i].gate = NCI_HCI_INVALID_GATE;
12962306a36Sopenharmony_ci			ndev->hci_dev->pipes[i].host = NCI_HCI_INVALID_HOST;
13062306a36Sopenharmony_ci		}
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* Fragment HCI data over NCI packet.
13562306a36Sopenharmony_ci * NFC Forum NCI 10.2.2 Data Exchange:
13662306a36Sopenharmony_ci * The payload of the Data Packets sent on the Logical Connection SHALL be
13762306a36Sopenharmony_ci * valid HCP packets, as defined within [ETSI_102622]. Each Data Packet SHALL
13862306a36Sopenharmony_ci * contain a single HCP packet. NCI Segmentation and Reassembly SHALL NOT be
13962306a36Sopenharmony_ci * applied to Data Messages in either direction. The HCI fragmentation mechanism
14062306a36Sopenharmony_ci * is used if required.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistatic int nci_hci_send_data(struct nci_dev *ndev, u8 pipe,
14362306a36Sopenharmony_ci			     const u8 data_type, const u8 *data,
14462306a36Sopenharmony_ci			     size_t data_len)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
14762306a36Sopenharmony_ci	struct sk_buff *skb;
14862306a36Sopenharmony_ci	int len, i, r;
14962306a36Sopenharmony_ci	u8 cb = pipe;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
15262306a36Sopenharmony_ci	if (!conn_info)
15362306a36Sopenharmony_ci		return -EPROTO;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	i = 0;
15662306a36Sopenharmony_ci	skb = nci_skb_alloc(ndev, conn_info->max_pkt_payload_len +
15762306a36Sopenharmony_ci			    NCI_DATA_HDR_SIZE, GFP_ATOMIC);
15862306a36Sopenharmony_ci	if (!skb)
15962306a36Sopenharmony_ci		return -ENOMEM;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	skb_reserve(skb, NCI_DATA_HDR_SIZE + 2);
16262306a36Sopenharmony_ci	*(u8 *)skb_push(skb, 1) = data_type;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	do {
16562306a36Sopenharmony_ci		/* If last packet add NCI_HFP_NO_CHAINING */
16662306a36Sopenharmony_ci		if (i + conn_info->max_pkt_payload_len -
16762306a36Sopenharmony_ci		    (skb->len + 1) >= data_len) {
16862306a36Sopenharmony_ci			cb |= NCI_HFP_NO_CHAINING;
16962306a36Sopenharmony_ci			len = data_len - i;
17062306a36Sopenharmony_ci		} else {
17162306a36Sopenharmony_ci			len = conn_info->max_pkt_payload_len - skb->len - 1;
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		*(u8 *)skb_push(skb, 1) = cb;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		if (len > 0)
17762306a36Sopenharmony_ci			skb_put_data(skb, data + i, len);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		r = nci_send_data(ndev, conn_info->conn_id, skb);
18062306a36Sopenharmony_ci		if (r < 0)
18162306a36Sopenharmony_ci			return r;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci		i += len;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		if (i < data_len) {
18662306a36Sopenharmony_ci			skb = nci_skb_alloc(ndev,
18762306a36Sopenharmony_ci					    conn_info->max_pkt_payload_len +
18862306a36Sopenharmony_ci					    NCI_DATA_HDR_SIZE, GFP_ATOMIC);
18962306a36Sopenharmony_ci			if (!skb)
19062306a36Sopenharmony_ci				return -ENOMEM;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci			skb_reserve(skb, NCI_DATA_HDR_SIZE + 1);
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci	} while (i < data_len);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	return i;
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void nci_hci_send_data_req(struct nci_dev *ndev, const void *opt)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	const struct nci_data *data = opt;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	nci_hci_send_data(ndev, data->pipe, data->cmd,
20462306a36Sopenharmony_ci			  data->data, data->data_len);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciint nci_hci_send_event(struct nci_dev *ndev, u8 gate, u8 event,
20862306a36Sopenharmony_ci		       const u8 *param, size_t param_len)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	u8 pipe = ndev->hci_dev->gate2pipe[gate];
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (pipe == NCI_HCI_INVALID_PIPE)
21362306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	return nci_hci_send_data(ndev, pipe,
21662306a36Sopenharmony_ci			NCI_HCP_HEADER(NCI_HCI_HCP_EVENT, event),
21762306a36Sopenharmony_ci			param, param_len);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_event);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ciint nci_hci_send_cmd(struct nci_dev *ndev, u8 gate, u8 cmd,
22262306a36Sopenharmony_ci		     const u8 *param, size_t param_len,
22362306a36Sopenharmony_ci		     struct sk_buff **skb)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	const struct nci_hcp_message *message;
22662306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
22762306a36Sopenharmony_ci	struct nci_data data;
22862306a36Sopenharmony_ci	int r;
22962306a36Sopenharmony_ci	u8 pipe = ndev->hci_dev->gate2pipe[gate];
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (pipe == NCI_HCI_INVALID_PIPE)
23262306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
23562306a36Sopenharmony_ci	if (!conn_info)
23662306a36Sopenharmony_ci		return -EPROTO;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	data.conn_id = conn_info->conn_id;
23962306a36Sopenharmony_ci	data.pipe = pipe;
24062306a36Sopenharmony_ci	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND, cmd);
24162306a36Sopenharmony_ci	data.data = param;
24262306a36Sopenharmony_ci	data.data_len = param_len;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	r = nci_request(ndev, nci_hci_send_data_req, &data,
24562306a36Sopenharmony_ci			msecs_to_jiffies(NCI_DATA_TIMEOUT));
24662306a36Sopenharmony_ci	if (r == NCI_STATUS_OK) {
24762306a36Sopenharmony_ci		message = (struct nci_hcp_message *)conn_info->rx_skb->data;
24862306a36Sopenharmony_ci		r = nci_hci_result_to_errno(
24962306a36Sopenharmony_ci			NCI_HCP_MSG_GET_CMD(message->header));
25062306a36Sopenharmony_ci		skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		if (!r && skb)
25362306a36Sopenharmony_ci			*skb = conn_info->rx_skb;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	return r;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_send_cmd);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciint nci_hci_clear_all_pipes(struct nci_dev *ndev)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	int r;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
26562306a36Sopenharmony_ci			     NCI_HCI_ADM_CLEAR_ALL_PIPE, NULL, 0, NULL);
26662306a36Sopenharmony_ci	if (r < 0)
26762306a36Sopenharmony_ci		return r;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	nci_hci_reset_pipes(ndev->hci_dev);
27062306a36Sopenharmony_ci	return r;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_clear_all_pipes);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_cistatic void nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
27562306a36Sopenharmony_ci				   u8 event, struct sk_buff *skb)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	if (ndev->ops->hci_event_received)
27862306a36Sopenharmony_ci		ndev->ops->hci_event_received(ndev, pipe, event, skb);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic void nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe,
28262306a36Sopenharmony_ci				 u8 cmd, struct sk_buff *skb)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	u8 gate = ndev->hci_dev->pipes[pipe].gate;
28562306a36Sopenharmony_ci	u8 status = NCI_HCI_ANY_OK | ~NCI_HCI_FRAGMENT;
28662306a36Sopenharmony_ci	u8 dest_gate, new_pipe;
28762306a36Sopenharmony_ci	struct nci_hci_create_pipe_resp *create_info;
28862306a36Sopenharmony_ci	struct nci_hci_delete_pipe_noti *delete_info;
28962306a36Sopenharmony_ci	struct nci_hci_all_pipe_cleared_noti *cleared_info;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	switch (cmd) {
29462306a36Sopenharmony_ci	case NCI_HCI_ADM_NOTIFY_PIPE_CREATED:
29562306a36Sopenharmony_ci		if (skb->len != 5) {
29662306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
29762306a36Sopenharmony_ci			goto exit;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci		create_info = (struct nci_hci_create_pipe_resp *)skb->data;
30062306a36Sopenharmony_ci		dest_gate = create_info->dest_gate;
30162306a36Sopenharmony_ci		new_pipe = create_info->pipe;
30262306a36Sopenharmony_ci		if (new_pipe >= NCI_HCI_MAX_PIPES) {
30362306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
30462306a36Sopenharmony_ci			goto exit;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		/* Save the new created pipe and bind with local gate,
30862306a36Sopenharmony_ci		 * the description for skb->data[3] is destination gate id
30962306a36Sopenharmony_ci		 * but since we received this cmd from host controller, we
31062306a36Sopenharmony_ci		 * are the destination and it is our local gate
31162306a36Sopenharmony_ci		 */
31262306a36Sopenharmony_ci		ndev->hci_dev->gate2pipe[dest_gate] = new_pipe;
31362306a36Sopenharmony_ci		ndev->hci_dev->pipes[new_pipe].gate = dest_gate;
31462306a36Sopenharmony_ci		ndev->hci_dev->pipes[new_pipe].host =
31562306a36Sopenharmony_ci						create_info->src_host;
31662306a36Sopenharmony_ci		break;
31762306a36Sopenharmony_ci	case NCI_HCI_ANY_OPEN_PIPE:
31862306a36Sopenharmony_ci		/* If the pipe is not created report an error */
31962306a36Sopenharmony_ci		if (gate == NCI_HCI_INVALID_GATE) {
32062306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
32162306a36Sopenharmony_ci			goto exit;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci		break;
32462306a36Sopenharmony_ci	case NCI_HCI_ADM_NOTIFY_PIPE_DELETED:
32562306a36Sopenharmony_ci		if (skb->len != 1) {
32662306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
32762306a36Sopenharmony_ci			goto exit;
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci		delete_info = (struct nci_hci_delete_pipe_noti *)skb->data;
33062306a36Sopenharmony_ci		if (delete_info->pipe >= NCI_HCI_MAX_PIPES) {
33162306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
33262306a36Sopenharmony_ci			goto exit;
33362306a36Sopenharmony_ci		}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci		ndev->hci_dev->pipes[delete_info->pipe].gate =
33662306a36Sopenharmony_ci						NCI_HCI_INVALID_GATE;
33762306a36Sopenharmony_ci		ndev->hci_dev->pipes[delete_info->pipe].host =
33862306a36Sopenharmony_ci						NCI_HCI_INVALID_HOST;
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case NCI_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
34162306a36Sopenharmony_ci		if (skb->len != 1) {
34262306a36Sopenharmony_ci			status = NCI_HCI_ANY_E_NOK;
34362306a36Sopenharmony_ci			goto exit;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		cleared_info =
34762306a36Sopenharmony_ci			(struct nci_hci_all_pipe_cleared_noti *)skb->data;
34862306a36Sopenharmony_ci		nci_hci_reset_pipes_per_host(ndev, cleared_info->host);
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	default:
35162306a36Sopenharmony_ci		pr_debug("Discarded unknown cmd %x to gate %x\n", cmd, gate);
35262306a36Sopenharmony_ci		break;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (ndev->ops->hci_cmd_received)
35662306a36Sopenharmony_ci		ndev->ops->hci_cmd_received(ndev, pipe, cmd, skb);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ciexit:
35962306a36Sopenharmony_ci	nci_hci_send_data(ndev, pipe, status, NULL, 0);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	kfree_skb(skb);
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic void nci_hci_resp_received(struct nci_dev *ndev, u8 pipe,
36562306a36Sopenharmony_ci				  struct sk_buff *skb)
36662306a36Sopenharmony_ci{
36762306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
37062306a36Sopenharmony_ci	if (!conn_info)
37162306a36Sopenharmony_ci		goto exit;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	conn_info->rx_skb = skb;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ciexit:
37662306a36Sopenharmony_ci	nci_req_complete(ndev, NCI_STATUS_OK);
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/* Receive hcp message for pipe, with type and cmd.
38062306a36Sopenharmony_ci * skb contains optional message data only.
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic void nci_hci_hcp_message_rx(struct nci_dev *ndev, u8 pipe,
38362306a36Sopenharmony_ci				   u8 type, u8 instruction, struct sk_buff *skb)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	switch (type) {
38662306a36Sopenharmony_ci	case NCI_HCI_HCP_RESPONSE:
38762306a36Sopenharmony_ci		nci_hci_resp_received(ndev, pipe, skb);
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case NCI_HCI_HCP_COMMAND:
39062306a36Sopenharmony_ci		nci_hci_cmd_received(ndev, pipe, instruction, skb);
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case NCI_HCI_HCP_EVENT:
39362306a36Sopenharmony_ci		nci_hci_event_received(ndev, pipe, instruction, skb);
39462306a36Sopenharmony_ci		break;
39562306a36Sopenharmony_ci	default:
39662306a36Sopenharmony_ci		pr_err("UNKNOWN MSG Type %d, instruction=%d\n",
39762306a36Sopenharmony_ci		       type, instruction);
39862306a36Sopenharmony_ci		kfree_skb(skb);
39962306a36Sopenharmony_ci		break;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	nci_req_complete(ndev, NCI_STATUS_OK);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void nci_hci_msg_rx_work(struct work_struct *work)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct nci_hci_dev *hdev =
40862306a36Sopenharmony_ci		container_of(work, struct nci_hci_dev, msg_rx_work);
40962306a36Sopenharmony_ci	struct sk_buff *skb;
41062306a36Sopenharmony_ci	const struct nci_hcp_message *message;
41162306a36Sopenharmony_ci	u8 pipe, type, instruction;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	for (; (skb = skb_dequeue(&hdev->msg_rx_queue)); kcov_remote_stop()) {
41462306a36Sopenharmony_ci		kcov_remote_start_common(skb_get_kcov_handle(skb));
41562306a36Sopenharmony_ci		pipe = NCI_HCP_MSG_GET_PIPE(skb->data[0]);
41662306a36Sopenharmony_ci		skb_pull(skb, NCI_HCI_HCP_PACKET_HEADER_LEN);
41762306a36Sopenharmony_ci		message = (struct nci_hcp_message *)skb->data;
41862306a36Sopenharmony_ci		type = NCI_HCP_MSG_GET_TYPE(message->header);
41962306a36Sopenharmony_ci		instruction = NCI_HCP_MSG_GET_CMD(message->header);
42062306a36Sopenharmony_ci		skb_pull(skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		nci_hci_hcp_message_rx(hdev->ndev, pipe,
42362306a36Sopenharmony_ci				       type, instruction, skb);
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_civoid nci_hci_data_received_cb(void *context,
42862306a36Sopenharmony_ci			      struct sk_buff *skb, int err)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	struct nci_dev *ndev = (struct nci_dev *)context;
43162306a36Sopenharmony_ci	struct nci_hcp_packet *packet;
43262306a36Sopenharmony_ci	u8 pipe, type;
43362306a36Sopenharmony_ci	struct sk_buff *hcp_skb;
43462306a36Sopenharmony_ci	struct sk_buff *frag_skb;
43562306a36Sopenharmony_ci	int msg_len;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	if (err) {
43862306a36Sopenharmony_ci		nci_req_complete(ndev, err);
43962306a36Sopenharmony_ci		return;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	packet = (struct nci_hcp_packet *)skb->data;
44362306a36Sopenharmony_ci	if ((packet->header & ~NCI_HCI_FRAGMENT) == 0) {
44462306a36Sopenharmony_ci		skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
44562306a36Sopenharmony_ci		return;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/* it's the last fragment. Does it need re-aggregation? */
44962306a36Sopenharmony_ci	if (skb_queue_len(&ndev->hci_dev->rx_hcp_frags)) {
45062306a36Sopenharmony_ci		pipe = NCI_HCP_MSG_GET_PIPE(packet->header);
45162306a36Sopenharmony_ci		skb_queue_tail(&ndev->hci_dev->rx_hcp_frags, skb);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		msg_len = 0;
45462306a36Sopenharmony_ci		skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) {
45562306a36Sopenharmony_ci			msg_len += (frag_skb->len -
45662306a36Sopenharmony_ci				    NCI_HCI_HCP_PACKET_HEADER_LEN);
45762306a36Sopenharmony_ci		}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		hcp_skb = nfc_alloc_recv_skb(NCI_HCI_HCP_PACKET_HEADER_LEN +
46062306a36Sopenharmony_ci					     msg_len, GFP_KERNEL);
46162306a36Sopenharmony_ci		if (!hcp_skb) {
46262306a36Sopenharmony_ci			nci_req_complete(ndev, -ENOMEM);
46362306a36Sopenharmony_ci			return;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		skb_put_u8(hcp_skb, pipe);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		skb_queue_walk(&ndev->hci_dev->rx_hcp_frags, frag_skb) {
46962306a36Sopenharmony_ci			msg_len = frag_skb->len - NCI_HCI_HCP_PACKET_HEADER_LEN;
47062306a36Sopenharmony_ci			skb_put_data(hcp_skb,
47162306a36Sopenharmony_ci				     frag_skb->data + NCI_HCI_HCP_PACKET_HEADER_LEN,
47262306a36Sopenharmony_ci				     msg_len);
47362306a36Sopenharmony_ci		}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci		skb_queue_purge(&ndev->hci_dev->rx_hcp_frags);
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		packet->header &= NCI_HCI_FRAGMENT;
47862306a36Sopenharmony_ci		hcp_skb = skb;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/* if this is a response, dispatch immediately to
48262306a36Sopenharmony_ci	 * unblock waiting cmd context. Otherwise, enqueue to dispatch
48362306a36Sopenharmony_ci	 * in separate context where handler can also execute command.
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	packet = (struct nci_hcp_packet *)hcp_skb->data;
48662306a36Sopenharmony_ci	type = NCI_HCP_MSG_GET_TYPE(packet->message.header);
48762306a36Sopenharmony_ci	if (type == NCI_HCI_HCP_RESPONSE) {
48862306a36Sopenharmony_ci		pipe = NCI_HCP_MSG_GET_PIPE(packet->header);
48962306a36Sopenharmony_ci		skb_pull(hcp_skb, NCI_HCI_HCP_PACKET_HEADER_LEN);
49062306a36Sopenharmony_ci		nci_hci_hcp_message_rx(ndev, pipe, type,
49162306a36Sopenharmony_ci				       NCI_STATUS_OK, hcp_skb);
49262306a36Sopenharmony_ci	} else {
49362306a36Sopenharmony_ci		skb_queue_tail(&ndev->hci_dev->msg_rx_queue, hcp_skb);
49462306a36Sopenharmony_ci		schedule_work(&ndev->hci_dev->msg_rx_work);
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ciint nci_hci_open_pipe(struct nci_dev *ndev, u8 pipe)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	struct nci_data data;
50162306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
50462306a36Sopenharmony_ci	if (!conn_info)
50562306a36Sopenharmony_ci		return -EPROTO;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	data.conn_id = conn_info->conn_id;
50862306a36Sopenharmony_ci	data.pipe = pipe;
50962306a36Sopenharmony_ci	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
51062306a36Sopenharmony_ci				       NCI_HCI_ANY_OPEN_PIPE);
51162306a36Sopenharmony_ci	data.data = NULL;
51262306a36Sopenharmony_ci	data.data_len = 0;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return nci_request(ndev, nci_hci_send_data_req, &data,
51562306a36Sopenharmony_ci			   msecs_to_jiffies(NCI_DATA_TIMEOUT));
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_open_pipe);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cistatic u8 nci_hci_create_pipe(struct nci_dev *ndev, u8 dest_host,
52062306a36Sopenharmony_ci			      u8 dest_gate, int *result)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	u8 pipe;
52362306a36Sopenharmony_ci	struct sk_buff *skb;
52462306a36Sopenharmony_ci	struct nci_hci_create_pipe_params params;
52562306a36Sopenharmony_ci	const struct nci_hci_create_pipe_resp *resp;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	pr_debug("gate=%d\n", dest_gate);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	params.src_gate = NCI_HCI_ADMIN_GATE;
53062306a36Sopenharmony_ci	params.dest_host = dest_host;
53162306a36Sopenharmony_ci	params.dest_gate = dest_gate;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	*result = nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
53462306a36Sopenharmony_ci				   NCI_HCI_ADM_CREATE_PIPE,
53562306a36Sopenharmony_ci				   (u8 *)&params, sizeof(params), &skb);
53662306a36Sopenharmony_ci	if (*result < 0)
53762306a36Sopenharmony_ci		return NCI_HCI_INVALID_PIPE;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	resp = (struct nci_hci_create_pipe_resp *)skb->data;
54062306a36Sopenharmony_ci	pipe = resp->pipe;
54162306a36Sopenharmony_ci	kfree_skb(skb);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	pr_debug("pipe created=%d\n", pipe);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	return pipe;
54662306a36Sopenharmony_ci}
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_cistatic int nci_hci_delete_pipe(struct nci_dev *ndev, u8 pipe)
54962306a36Sopenharmony_ci{
55062306a36Sopenharmony_ci	return nci_hci_send_cmd(ndev, NCI_HCI_ADMIN_GATE,
55162306a36Sopenharmony_ci				NCI_HCI_ADM_DELETE_PIPE, &pipe, 1, NULL);
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ciint nci_hci_set_param(struct nci_dev *ndev, u8 gate, u8 idx,
55562306a36Sopenharmony_ci		      const u8 *param, size_t param_len)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	const struct nci_hcp_message *message;
55862306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
55962306a36Sopenharmony_ci	struct nci_data data;
56062306a36Sopenharmony_ci	int r;
56162306a36Sopenharmony_ci	u8 *tmp;
56262306a36Sopenharmony_ci	u8 pipe = ndev->hci_dev->gate2pipe[gate];
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	pr_debug("idx=%d to gate %d\n", idx, gate);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	if (pipe == NCI_HCI_INVALID_PIPE)
56762306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
57062306a36Sopenharmony_ci	if (!conn_info)
57162306a36Sopenharmony_ci		return -EPROTO;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	tmp = kmalloc(1 + param_len, GFP_KERNEL);
57462306a36Sopenharmony_ci	if (!tmp)
57562306a36Sopenharmony_ci		return -ENOMEM;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	*tmp = idx;
57862306a36Sopenharmony_ci	memcpy(tmp + 1, param, param_len);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	data.conn_id = conn_info->conn_id;
58162306a36Sopenharmony_ci	data.pipe = pipe;
58262306a36Sopenharmony_ci	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
58362306a36Sopenharmony_ci				       NCI_HCI_ANY_SET_PARAMETER);
58462306a36Sopenharmony_ci	data.data = tmp;
58562306a36Sopenharmony_ci	data.data_len = param_len + 1;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	r = nci_request(ndev, nci_hci_send_data_req, &data,
58862306a36Sopenharmony_ci			msecs_to_jiffies(NCI_DATA_TIMEOUT));
58962306a36Sopenharmony_ci	if (r == NCI_STATUS_OK) {
59062306a36Sopenharmony_ci		message = (struct nci_hcp_message *)conn_info->rx_skb->data;
59162306a36Sopenharmony_ci		r = nci_hci_result_to_errno(
59262306a36Sopenharmony_ci			NCI_HCP_MSG_GET_CMD(message->header));
59362306a36Sopenharmony_ci		skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	kfree(tmp);
59762306a36Sopenharmony_ci	return r;
59862306a36Sopenharmony_ci}
59962306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_set_param);
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ciint nci_hci_get_param(struct nci_dev *ndev, u8 gate, u8 idx,
60262306a36Sopenharmony_ci		      struct sk_buff **skb)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	const struct nci_hcp_message *message;
60562306a36Sopenharmony_ci	const struct nci_conn_info *conn_info;
60662306a36Sopenharmony_ci	struct nci_data data;
60762306a36Sopenharmony_ci	int r;
60862306a36Sopenharmony_ci	u8 pipe = ndev->hci_dev->gate2pipe[gate];
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	pr_debug("idx=%d to gate %d\n", idx, gate);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	if (pipe == NCI_HCI_INVALID_PIPE)
61362306a36Sopenharmony_ci		return -EADDRNOTAVAIL;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
61662306a36Sopenharmony_ci	if (!conn_info)
61762306a36Sopenharmony_ci		return -EPROTO;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	data.conn_id = conn_info->conn_id;
62062306a36Sopenharmony_ci	data.pipe = pipe;
62162306a36Sopenharmony_ci	data.cmd = NCI_HCP_HEADER(NCI_HCI_HCP_COMMAND,
62262306a36Sopenharmony_ci				  NCI_HCI_ANY_GET_PARAMETER);
62362306a36Sopenharmony_ci	data.data = &idx;
62462306a36Sopenharmony_ci	data.data_len = 1;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	r = nci_request(ndev, nci_hci_send_data_req, &data,
62762306a36Sopenharmony_ci			msecs_to_jiffies(NCI_DATA_TIMEOUT));
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	if (r == NCI_STATUS_OK) {
63062306a36Sopenharmony_ci		message = (struct nci_hcp_message *)conn_info->rx_skb->data;
63162306a36Sopenharmony_ci		r = nci_hci_result_to_errno(
63262306a36Sopenharmony_ci			NCI_HCP_MSG_GET_CMD(message->header));
63362306a36Sopenharmony_ci		skb_pull(conn_info->rx_skb, NCI_HCI_HCP_MESSAGE_HEADER_LEN);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci		if (!r && skb)
63662306a36Sopenharmony_ci			*skb = conn_info->rx_skb;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return r;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_get_param);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ciint nci_hci_connect_gate(struct nci_dev *ndev,
64462306a36Sopenharmony_ci			 u8 dest_host, u8 dest_gate, u8 pipe)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	bool pipe_created = false;
64762306a36Sopenharmony_ci	int r;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (pipe == NCI_HCI_DO_NOT_OPEN_PIPE)
65062306a36Sopenharmony_ci		return 0;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (ndev->hci_dev->gate2pipe[dest_gate] != NCI_HCI_INVALID_PIPE)
65362306a36Sopenharmony_ci		return -EADDRINUSE;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (pipe != NCI_HCI_INVALID_PIPE)
65662306a36Sopenharmony_ci		goto open_pipe;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	switch (dest_gate) {
65962306a36Sopenharmony_ci	case NCI_HCI_LINK_MGMT_GATE:
66062306a36Sopenharmony_ci		pipe = NCI_HCI_LINK_MGMT_PIPE;
66162306a36Sopenharmony_ci	break;
66262306a36Sopenharmony_ci	case NCI_HCI_ADMIN_GATE:
66362306a36Sopenharmony_ci		pipe = NCI_HCI_ADMIN_PIPE;
66462306a36Sopenharmony_ci	break;
66562306a36Sopenharmony_ci	default:
66662306a36Sopenharmony_ci		pipe = nci_hci_create_pipe(ndev, dest_host, dest_gate, &r);
66762306a36Sopenharmony_ci		if (pipe == NCI_HCI_INVALID_PIPE)
66862306a36Sopenharmony_ci			return r;
66962306a36Sopenharmony_ci		pipe_created = true;
67062306a36Sopenharmony_ci		break;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ciopen_pipe:
67462306a36Sopenharmony_ci	r = nci_hci_open_pipe(ndev, pipe);
67562306a36Sopenharmony_ci	if (r < 0) {
67662306a36Sopenharmony_ci		if (pipe_created) {
67762306a36Sopenharmony_ci			if (nci_hci_delete_pipe(ndev, pipe) < 0) {
67862306a36Sopenharmony_ci				/* TODO: Cannot clean by deleting pipe...
67962306a36Sopenharmony_ci				 * -> inconsistent state
68062306a36Sopenharmony_ci				 */
68162306a36Sopenharmony_ci			}
68262306a36Sopenharmony_ci		}
68362306a36Sopenharmony_ci		return r;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	ndev->hci_dev->pipes[pipe].gate = dest_gate;
68762306a36Sopenharmony_ci	ndev->hci_dev->pipes[pipe].host = dest_host;
68862306a36Sopenharmony_ci	ndev->hci_dev->gate2pipe[dest_gate] = pipe;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	return 0;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_connect_gate);
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_cistatic int nci_hci_dev_connect_gates(struct nci_dev *ndev,
69562306a36Sopenharmony_ci				     u8 gate_count,
69662306a36Sopenharmony_ci				     const struct nci_hci_gate *gates)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	int r;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	while (gate_count--) {
70162306a36Sopenharmony_ci		r = nci_hci_connect_gate(ndev, gates->dest_host,
70262306a36Sopenharmony_ci					 gates->gate, gates->pipe);
70362306a36Sopenharmony_ci		if (r < 0)
70462306a36Sopenharmony_ci			return r;
70562306a36Sopenharmony_ci		gates++;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	return 0;
70962306a36Sopenharmony_ci}
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ciint nci_hci_dev_session_init(struct nci_dev *ndev)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	struct nci_conn_info *conn_info;
71462306a36Sopenharmony_ci	struct sk_buff *skb;
71562306a36Sopenharmony_ci	int r;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	ndev->hci_dev->count_pipes = 0;
71862306a36Sopenharmony_ci	ndev->hci_dev->expected_pipes = 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
72162306a36Sopenharmony_ci	if (!conn_info)
72262306a36Sopenharmony_ci		return -EPROTO;
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	conn_info->data_exchange_cb = nci_hci_data_received_cb;
72562306a36Sopenharmony_ci	conn_info->data_exchange_cb_context = ndev;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	nci_hci_reset_pipes(ndev->hci_dev);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	if (ndev->hci_dev->init_data.gates[0].gate != NCI_HCI_ADMIN_GATE)
73062306a36Sopenharmony_ci		return -EPROTO;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	r = nci_hci_connect_gate(ndev,
73362306a36Sopenharmony_ci				 ndev->hci_dev->init_data.gates[0].dest_host,
73462306a36Sopenharmony_ci				 ndev->hci_dev->init_data.gates[0].gate,
73562306a36Sopenharmony_ci				 ndev->hci_dev->init_data.gates[0].pipe);
73662306a36Sopenharmony_ci	if (r < 0)
73762306a36Sopenharmony_ci		return r;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
74062306a36Sopenharmony_ci			      NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY, &skb);
74162306a36Sopenharmony_ci	if (r < 0)
74262306a36Sopenharmony_ci		return r;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (skb->len &&
74562306a36Sopenharmony_ci	    skb->len == strlen(ndev->hci_dev->init_data.session_id) &&
74662306a36Sopenharmony_ci	    !memcmp(ndev->hci_dev->init_data.session_id, skb->data, skb->len) &&
74762306a36Sopenharmony_ci	    ndev->ops->hci_load_session) {
74862306a36Sopenharmony_ci		/* Restore gate<->pipe table from some proprietary location. */
74962306a36Sopenharmony_ci		r = ndev->ops->hci_load_session(ndev);
75062306a36Sopenharmony_ci	} else {
75162306a36Sopenharmony_ci		r = nci_hci_clear_all_pipes(ndev);
75262306a36Sopenharmony_ci		if (r < 0)
75362306a36Sopenharmony_ci			goto exit;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci		r = nci_hci_dev_connect_gates(ndev,
75662306a36Sopenharmony_ci					      ndev->hci_dev->init_data.gate_count,
75762306a36Sopenharmony_ci					      ndev->hci_dev->init_data.gates);
75862306a36Sopenharmony_ci		if (r < 0)
75962306a36Sopenharmony_ci			goto exit;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
76262306a36Sopenharmony_ci				      NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY,
76362306a36Sopenharmony_ci				      ndev->hci_dev->init_data.session_id,
76462306a36Sopenharmony_ci				      strlen(ndev->hci_dev->init_data.session_id));
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ciexit:
76862306a36Sopenharmony_ci	kfree_skb(skb);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return r;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ciEXPORT_SYMBOL(nci_hci_dev_session_init);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistruct nci_hci_dev *nci_hci_allocate(struct nci_dev *ndev)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	struct nci_hci_dev *hdev;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
77962306a36Sopenharmony_ci	if (!hdev)
78062306a36Sopenharmony_ci		return NULL;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	skb_queue_head_init(&hdev->rx_hcp_frags);
78362306a36Sopenharmony_ci	INIT_WORK(&hdev->msg_rx_work, nci_hci_msg_rx_work);
78462306a36Sopenharmony_ci	skb_queue_head_init(&hdev->msg_rx_queue);
78562306a36Sopenharmony_ci	hdev->ndev = ndev;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	return hdev;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_civoid nci_hci_deallocate(struct nci_dev *ndev)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	kfree(ndev->hci_dev);
79362306a36Sopenharmony_ci}
794