162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Secure Element driver for STMicroelectronics NFC NCI chip
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/nfc.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <net/nfc/nci.h>
1262306a36Sopenharmony_ci#include <net/nfc/nci_core.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "st-nci.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistruct st_nci_pipe_info {
1762306a36Sopenharmony_ci	u8 pipe_state;
1862306a36Sopenharmony_ci	u8 src_host_id;
1962306a36Sopenharmony_ci	u8 src_gate_id;
2062306a36Sopenharmony_ci	u8 dst_host_id;
2162306a36Sopenharmony_ci	u8 dst_gate_id;
2262306a36Sopenharmony_ci} __packed;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* Hosts */
2562306a36Sopenharmony_ci#define ST_NCI_HOST_CONTROLLER_ID     0x00
2662306a36Sopenharmony_ci#define ST_NCI_TERMINAL_HOST_ID       0x01
2762306a36Sopenharmony_ci#define ST_NCI_UICC_HOST_ID           0x02
2862306a36Sopenharmony_ci#define ST_NCI_ESE_HOST_ID            0xc0
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Gates */
3162306a36Sopenharmony_ci#define ST_NCI_APDU_READER_GATE       0xf0
3262306a36Sopenharmony_ci#define ST_NCI_CONNECTIVITY_GATE      0x41
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* Pipes */
3562306a36Sopenharmony_ci#define ST_NCI_DEVICE_MGNT_PIPE               0x02
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Connectivity pipe only */
3862306a36Sopenharmony_ci#define ST_NCI_SE_COUNT_PIPE_UICC             0x01
3962306a36Sopenharmony_ci/* Connectivity + APDU Reader pipe */
4062306a36Sopenharmony_ci#define ST_NCI_SE_COUNT_PIPE_EMBEDDED         0x02
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define ST_NCI_SE_TO_HOT_PLUG			1000 /* msecs */
4362306a36Sopenharmony_ci#define ST_NCI_SE_TO_PIPES			2000
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(x)   (x->data[0] & 0x80)
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define NCI_HCI_APDU_PARAM_ATR                     0x01
4862306a36Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY       0x01
4962306a36Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_WHITELIST              0x03
5062306a36Sopenharmony_ci#define NCI_HCI_ADMIN_PARAM_HOST_LIST              0x04
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define ST_NCI_EVT_SE_HARD_RESET		0x20
5362306a36Sopenharmony_ci#define ST_NCI_EVT_TRANSMIT_DATA		0x10
5462306a36Sopenharmony_ci#define ST_NCI_EVT_WTX_REQUEST			0x11
5562306a36Sopenharmony_ci#define ST_NCI_EVT_SE_SOFT_RESET		0x11
5662306a36Sopenharmony_ci#define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER	0x21
5762306a36Sopenharmony_ci#define ST_NCI_EVT_HOT_PLUG			0x03
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#define ST_NCI_SE_MODE_OFF                    0x00
6062306a36Sopenharmony_ci#define ST_NCI_SE_MODE_ON                     0x01
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define ST_NCI_EVT_CONNECTIVITY       0x10
6362306a36Sopenharmony_ci#define ST_NCI_EVT_TRANSACTION        0x12
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define ST_NCI_DM_GETINFO             0x13
6662306a36Sopenharmony_ci#define ST_NCI_DM_GETINFO_PIPE_LIST   0x02
6762306a36Sopenharmony_ci#define ST_NCI_DM_GETINFO_PIPE_INFO   0x01
6862306a36Sopenharmony_ci#define ST_NCI_DM_PIPE_CREATED        0x02
6962306a36Sopenharmony_ci#define ST_NCI_DM_PIPE_OPEN           0x04
7062306a36Sopenharmony_ci#define ST_NCI_DM_RF_ACTIVE           0x80
7162306a36Sopenharmony_ci#define ST_NCI_DM_DISCONNECT          0x30
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci#define ST_NCI_DM_IS_PIPE_OPEN(p) \
7462306a36Sopenharmony_ci	((p & 0x0f) == (ST_NCI_DM_PIPE_CREATED | ST_NCI_DM_PIPE_OPEN))
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define ST_NCI_ATR_DEFAULT_BWI        0x04
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * WT = 2^BWI/10[s], convert into msecs and add a secure
8062306a36Sopenharmony_ci * room by increasing by 2 this timeout
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci#define ST_NCI_BWI_TO_TIMEOUT(x)      ((1 << x) * 200)
8362306a36Sopenharmony_ci#define ST_NCI_ATR_GET_Y_FROM_TD(x)   (x >> 4)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* If TA is present bit 0 is set */
8662306a36Sopenharmony_ci#define ST_NCI_ATR_TA_PRESENT(x) (x & 0x01)
8762306a36Sopenharmony_ci/* If TB is present bit 1 is set */
8862306a36Sopenharmony_ci#define ST_NCI_ATR_TB_PRESENT(x) (x & 0x02)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define ST_NCI_NUM_DEVICES           256
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic DECLARE_BITMAP(dev_mask, ST_NCI_NUM_DEVICES);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Here are the mandatory pipe for st_nci */
9562306a36Sopenharmony_cistatic struct nci_hci_gate st_nci_gates[] = {
9662306a36Sopenharmony_ci	{NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE,
9762306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
9862306a36Sopenharmony_ci	{NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE,
9962306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
10062306a36Sopenharmony_ci	{ST_NCI_DEVICE_MGNT_GATE, ST_NCI_DEVICE_MGNT_PIPE,
10162306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	{NCI_HCI_IDENTITY_MGMT_GATE, NCI_HCI_INVALID_PIPE,
10462306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* Secure element pipes are created by secure element host */
10762306a36Sopenharmony_ci	{ST_NCI_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
10862306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
10962306a36Sopenharmony_ci	{ST_NCI_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE,
11062306a36Sopenharmony_ci					ST_NCI_HOST_CONTROLLER_ID},
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic u8 st_nci_se_get_bwi(struct nci_dev *ndev)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int i;
11662306a36Sopenharmony_ci	u8 td;
11762306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
12062306a36Sopenharmony_ci	for (i = 1; i < ST_NCI_ESE_MAX_LENGTH; i++) {
12162306a36Sopenharmony_ci		td = ST_NCI_ATR_GET_Y_FROM_TD(info->se_info.atr[i]);
12262306a36Sopenharmony_ci		if (ST_NCI_ATR_TA_PRESENT(td))
12362306a36Sopenharmony_ci			i++;
12462306a36Sopenharmony_ci		if (ST_NCI_ATR_TB_PRESENT(td)) {
12562306a36Sopenharmony_ci			i++;
12662306a36Sopenharmony_ci			return info->se_info.atr[i] >> 4;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci	return ST_NCI_ATR_DEFAULT_BWI;
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void st_nci_se_get_atr(struct nci_dev *ndev)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
13562306a36Sopenharmony_ci	int r;
13662306a36Sopenharmony_ci	struct sk_buff *skb;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	r = nci_hci_get_param(ndev, ST_NCI_APDU_READER_GATE,
13962306a36Sopenharmony_ci				NCI_HCI_APDU_PARAM_ATR, &skb);
14062306a36Sopenharmony_ci	if (r < 0)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (skb->len <= ST_NCI_ESE_MAX_LENGTH) {
14462306a36Sopenharmony_ci		memcpy(info->se_info.atr, skb->data, skb->len);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		info->se_info.wt_timeout =
14762306a36Sopenharmony_ci			ST_NCI_BWI_TO_TIMEOUT(st_nci_se_get_bwi(ndev));
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	kfree_skb(skb);
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciint st_nci_hci_load_session(struct nci_dev *ndev)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	int i, j, r;
15562306a36Sopenharmony_ci	struct sk_buff *skb_pipe_list, *skb_pipe_info;
15662306a36Sopenharmony_ci	struct st_nci_pipe_info *dm_pipe_info;
15762306a36Sopenharmony_ci	u8 pipe_list[] = { ST_NCI_DM_GETINFO_PIPE_LIST,
15862306a36Sopenharmony_ci			ST_NCI_TERMINAL_HOST_ID};
15962306a36Sopenharmony_ci	u8 pipe_info[] = { ST_NCI_DM_GETINFO_PIPE_INFO,
16062306a36Sopenharmony_ci			ST_NCI_TERMINAL_HOST_ID, 0};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* On ST_NCI device pipes number are dynamics
16362306a36Sopenharmony_ci	 * If pipes are already created, hci_dev_up will fail.
16462306a36Sopenharmony_ci	 * Doing a clear all pipe is a bad idea because:
16562306a36Sopenharmony_ci	 * - It does useless EEPROM cycling
16662306a36Sopenharmony_ci	 * - It might cause issue for secure elements support
16762306a36Sopenharmony_ci	 * (such as removing connectivity or APDU reader pipe)
16862306a36Sopenharmony_ci	 * A better approach on ST_NCI is to:
16962306a36Sopenharmony_ci	 * - get a pipe list for each host.
17062306a36Sopenharmony_ci	 * (eg: ST_NCI_HOST_CONTROLLER_ID for now).
17162306a36Sopenharmony_ci	 * (TODO Later on UICC HOST and eSE HOST)
17262306a36Sopenharmony_ci	 * - get pipe information
17362306a36Sopenharmony_ci	 * - match retrieved pipe list in st_nci_gates
17462306a36Sopenharmony_ci	 * ST_NCI_DEVICE_MGNT_GATE is a proprietary gate
17562306a36Sopenharmony_ci	 * with ST_NCI_DEVICE_MGNT_PIPE.
17662306a36Sopenharmony_ci	 * Pipe can be closed and need to be open.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID,
17962306a36Sopenharmony_ci				ST_NCI_DEVICE_MGNT_GATE,
18062306a36Sopenharmony_ci				ST_NCI_DEVICE_MGNT_PIPE);
18162306a36Sopenharmony_ci	if (r < 0)
18262306a36Sopenharmony_ci		return r;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* Get pipe list */
18562306a36Sopenharmony_ci	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
18662306a36Sopenharmony_ci			ST_NCI_DM_GETINFO, pipe_list, sizeof(pipe_list),
18762306a36Sopenharmony_ci			&skb_pipe_list);
18862306a36Sopenharmony_ci	if (r < 0)
18962306a36Sopenharmony_ci		return r;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* Complete the existing gate_pipe table */
19262306a36Sopenharmony_ci	for (i = 0; i < skb_pipe_list->len; i++) {
19362306a36Sopenharmony_ci		pipe_info[2] = skb_pipe_list->data[i];
19462306a36Sopenharmony_ci		r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
19562306a36Sopenharmony_ci					ST_NCI_DM_GETINFO, pipe_info,
19662306a36Sopenharmony_ci					sizeof(pipe_info), &skb_pipe_info);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		if (r)
19962306a36Sopenharmony_ci			continue;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		/*
20262306a36Sopenharmony_ci		 * Match pipe ID and gate ID
20362306a36Sopenharmony_ci		 * Output format from ST21NFC_DM_GETINFO is:
20462306a36Sopenharmony_ci		 * - pipe state (1byte)
20562306a36Sopenharmony_ci		 * - source hid (1byte)
20662306a36Sopenharmony_ci		 * - source gid (1byte)
20762306a36Sopenharmony_ci		 * - destination hid (1byte)
20862306a36Sopenharmony_ci		 * - destination gid (1byte)
20962306a36Sopenharmony_ci		 */
21062306a36Sopenharmony_ci		dm_pipe_info = (struct st_nci_pipe_info *)skb_pipe_info->data;
21162306a36Sopenharmony_ci		if (dm_pipe_info->dst_gate_id == ST_NCI_APDU_READER_GATE &&
21262306a36Sopenharmony_ci		    dm_pipe_info->src_host_id == ST_NCI_UICC_HOST_ID) {
21362306a36Sopenharmony_ci			pr_err("Unexpected apdu_reader pipe on host %x\n",
21462306a36Sopenharmony_ci			       dm_pipe_info->src_host_id);
21562306a36Sopenharmony_ci			kfree_skb(skb_pipe_info);
21662306a36Sopenharmony_ci			continue;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci		for (j = 3; (j < ARRAY_SIZE(st_nci_gates)) &&
22062306a36Sopenharmony_ci		     (st_nci_gates[j].gate != dm_pipe_info->dst_gate_id); j++)
22162306a36Sopenharmony_ci			;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		if (j < ARRAY_SIZE(st_nci_gates) &&
22462306a36Sopenharmony_ci		    st_nci_gates[j].gate == dm_pipe_info->dst_gate_id &&
22562306a36Sopenharmony_ci		    ST_NCI_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) {
22662306a36Sopenharmony_ci			ndev->hci_dev->init_data.gates[j].pipe = pipe_info[2];
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci			ndev->hci_dev->gate2pipe[st_nci_gates[j].gate] =
22962306a36Sopenharmony_ci						pipe_info[2];
23062306a36Sopenharmony_ci			ndev->hci_dev->pipes[pipe_info[2]].gate =
23162306a36Sopenharmony_ci						st_nci_gates[j].gate;
23262306a36Sopenharmony_ci			ndev->hci_dev->pipes[pipe_info[2]].host =
23362306a36Sopenharmony_ci						dm_pipe_info->src_host_id;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci		kfree_skb(skb_pipe_info);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/*
23962306a36Sopenharmony_ci	 * 3 gates have a well known pipe ID. Only NCI_HCI_LINK_MGMT_GATE
24062306a36Sopenharmony_ci	 * is not yet open at this stage.
24162306a36Sopenharmony_ci	 */
24262306a36Sopenharmony_ci	r = nci_hci_connect_gate(ndev, ST_NCI_HOST_CONTROLLER_ID,
24362306a36Sopenharmony_ci				 NCI_HCI_LINK_MGMT_GATE,
24462306a36Sopenharmony_ci				 NCI_HCI_LINK_MGMT_PIPE);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	kfree_skb(skb_pipe_list);
24762306a36Sopenharmony_ci	return r;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_hci_load_session);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic void st_nci_hci_admin_event_received(struct nci_dev *ndev,
25262306a36Sopenharmony_ci					      u8 event, struct sk_buff *skb)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	switch (event) {
25762306a36Sopenharmony_ci	case ST_NCI_EVT_HOT_PLUG:
25862306a36Sopenharmony_ci		if (info->se_info.se_active) {
25962306a36Sopenharmony_ci			if (!ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
26062306a36Sopenharmony_ci				del_timer_sync(&info->se_info.se_active_timer);
26162306a36Sopenharmony_ci				info->se_info.se_active = false;
26262306a36Sopenharmony_ci				complete(&info->se_info.req_completion);
26362306a36Sopenharmony_ci			} else {
26462306a36Sopenharmony_ci				mod_timer(&info->se_info.se_active_timer,
26562306a36Sopenharmony_ci				      jiffies +
26662306a36Sopenharmony_ci				      msecs_to_jiffies(ST_NCI_SE_TO_PIPES));
26762306a36Sopenharmony_ci			}
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	break;
27062306a36Sopenharmony_ci	default:
27162306a36Sopenharmony_ci		nfc_err(&ndev->nfc_dev->dev, "Unexpected event on admin gate\n");
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int st_nci_hci_apdu_reader_event_received(struct nci_dev *ndev,
27662306a36Sopenharmony_ci						   u8 event,
27762306a36Sopenharmony_ci						   struct sk_buff *skb)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	pr_debug("apdu reader gate event: %x\n", event);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	switch (event) {
28462306a36Sopenharmony_ci	case ST_NCI_EVT_TRANSMIT_DATA:
28562306a36Sopenharmony_ci		del_timer_sync(&info->se_info.bwi_timer);
28662306a36Sopenharmony_ci		info->se_info.bwi_active = false;
28762306a36Sopenharmony_ci		info->se_info.cb(info->se_info.cb_context,
28862306a36Sopenharmony_ci				 skb->data, skb->len, 0);
28962306a36Sopenharmony_ci	break;
29062306a36Sopenharmony_ci	case ST_NCI_EVT_WTX_REQUEST:
29162306a36Sopenharmony_ci		mod_timer(&info->se_info.bwi_timer, jiffies +
29262306a36Sopenharmony_ci			  msecs_to_jiffies(info->se_info.wt_timeout));
29362306a36Sopenharmony_ci	break;
29462306a36Sopenharmony_ci	default:
29562306a36Sopenharmony_ci		nfc_err(&ndev->nfc_dev->dev, "Unexpected event on apdu reader gate\n");
29662306a36Sopenharmony_ci		return 1;
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	kfree_skb(skb);
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * Returns:
30562306a36Sopenharmony_ci * <= 0: driver handled the event, skb consumed
30662306a36Sopenharmony_ci *    1: driver does not handle the event, please do standard processing
30762306a36Sopenharmony_ci */
30862306a36Sopenharmony_cistatic int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
30962306a36Sopenharmony_ci						u8 host, u8 event,
31062306a36Sopenharmony_ci						struct sk_buff *skb)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int r = 0;
31362306a36Sopenharmony_ci	struct device *dev = &ndev->nfc_dev->dev;
31462306a36Sopenharmony_ci	struct nfc_evt_transaction *transaction;
31562306a36Sopenharmony_ci	u32 aid_len;
31662306a36Sopenharmony_ci	u8 params_len;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	pr_debug("connectivity gate event: %x\n", event);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	switch (event) {
32162306a36Sopenharmony_ci	case ST_NCI_EVT_CONNECTIVITY:
32262306a36Sopenharmony_ci		r = nfc_se_connectivity(ndev->nfc_dev, host);
32362306a36Sopenharmony_ci	break;
32462306a36Sopenharmony_ci	case ST_NCI_EVT_TRANSACTION:
32562306a36Sopenharmony_ci		/* According to specification etsi 102 622
32662306a36Sopenharmony_ci		 * 11.2.2.4 EVT_TRANSACTION Table 52
32762306a36Sopenharmony_ci		 * Description  Tag     Length
32862306a36Sopenharmony_ci		 * AID          81      5 to 16
32962306a36Sopenharmony_ci		 * PARAMETERS   82      0 to 255
33062306a36Sopenharmony_ci		 *
33162306a36Sopenharmony_ci		 * The key differences are aid storage length is variably sized
33262306a36Sopenharmony_ci		 * in the packet, but fixed in nfc_evt_transaction, and that
33362306a36Sopenharmony_ci		 * the aid_len is u8 in the packet, but u32 in the structure,
33462306a36Sopenharmony_ci		 * and the tags in the packet are not included in
33562306a36Sopenharmony_ci		 * nfc_evt_transaction.
33662306a36Sopenharmony_ci		 *
33762306a36Sopenharmony_ci		 * size(b):  1          1       5-16 1             1           0-255
33862306a36Sopenharmony_ci		 * offset:   0          1       2    aid_len + 2   aid_len + 3 aid_len + 4
33962306a36Sopenharmony_ci		 * mem name: aid_tag(M) aid_len aid  params_tag(M) params_len  params
34062306a36Sopenharmony_ci		 * example:  0x81       5-16    X    0x82          0-255       X
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		if (skb->len < 2 || skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
34362306a36Sopenharmony_ci			return -EPROTO;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		aid_len = skb->data[1];
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		if (skb->len < aid_len + 4 ||
34862306a36Sopenharmony_ci		    aid_len > sizeof(transaction->aid))
34962306a36Sopenharmony_ci			return -EPROTO;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci		params_len = skb->data[aid_len + 3];
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		/* Verify PARAMETERS tag is (82), and final check that there is
35462306a36Sopenharmony_ci		 * enough space in the packet to read everything.
35562306a36Sopenharmony_ci		 */
35662306a36Sopenharmony_ci		if (skb->data[aid_len + 2] != NFC_EVT_TRANSACTION_PARAMS_TAG ||
35762306a36Sopenharmony_ci		    skb->len < aid_len + 4 + params_len)
35862306a36Sopenharmony_ci			return -EPROTO;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci		transaction = devm_kzalloc(dev, sizeof(*transaction) +
36162306a36Sopenharmony_ci					   params_len, GFP_KERNEL);
36262306a36Sopenharmony_ci		if (!transaction)
36362306a36Sopenharmony_ci			return -ENOMEM;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci		transaction->aid_len = aid_len;
36662306a36Sopenharmony_ci		transaction->params_len = params_len;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci		memcpy(transaction->aid, &skb->data[2], aid_len);
36962306a36Sopenharmony_ci		memcpy(transaction->params, &skb->data[aid_len + 4],
37062306a36Sopenharmony_ci		       params_len);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci		r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
37362306a36Sopenharmony_ci		break;
37462306a36Sopenharmony_ci	default:
37562306a36Sopenharmony_ci		nfc_err(&ndev->nfc_dev->dev, "Unexpected event on connectivity gate\n");
37662306a36Sopenharmony_ci		return 1;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	kfree_skb(skb);
37962306a36Sopenharmony_ci	return r;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_civoid st_nci_hci_event_received(struct nci_dev *ndev, u8 pipe,
38362306a36Sopenharmony_ci				 u8 event, struct sk_buff *skb)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	u8 gate = ndev->hci_dev->pipes[pipe].gate;
38662306a36Sopenharmony_ci	u8 host = ndev->hci_dev->pipes[pipe].host;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	switch (gate) {
38962306a36Sopenharmony_ci	case NCI_HCI_ADMIN_GATE:
39062306a36Sopenharmony_ci		st_nci_hci_admin_event_received(ndev, event, skb);
39162306a36Sopenharmony_ci	break;
39262306a36Sopenharmony_ci	case ST_NCI_APDU_READER_GATE:
39362306a36Sopenharmony_ci		st_nci_hci_apdu_reader_event_received(ndev, event, skb);
39462306a36Sopenharmony_ci	break;
39562306a36Sopenharmony_ci	case ST_NCI_CONNECTIVITY_GATE:
39662306a36Sopenharmony_ci		st_nci_hci_connectivity_event_received(ndev, host, event, skb);
39762306a36Sopenharmony_ci	break;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_hci_event_received);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_civoid st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
40362306a36Sopenharmony_ci			       struct sk_buff *skb)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
40662306a36Sopenharmony_ci	u8 gate = ndev->hci_dev->pipes[pipe].gate;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	pr_debug("cmd: %x\n", cmd);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	switch (cmd) {
41162306a36Sopenharmony_ci	case NCI_HCI_ANY_OPEN_PIPE:
41262306a36Sopenharmony_ci		if (gate != ST_NCI_APDU_READER_GATE &&
41362306a36Sopenharmony_ci		    ndev->hci_dev->pipes[pipe].host != ST_NCI_UICC_HOST_ID)
41462306a36Sopenharmony_ci			ndev->hci_dev->count_pipes++;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci		if (ndev->hci_dev->count_pipes ==
41762306a36Sopenharmony_ci		    ndev->hci_dev->expected_pipes) {
41862306a36Sopenharmony_ci			del_timer_sync(&info->se_info.se_active_timer);
41962306a36Sopenharmony_ci			info->se_info.se_active = false;
42062306a36Sopenharmony_ci			ndev->hci_dev->count_pipes = 0;
42162306a36Sopenharmony_ci			complete(&info->se_info.req_completion);
42262306a36Sopenharmony_ci		}
42362306a36Sopenharmony_ci	break;
42462306a36Sopenharmony_ci	}
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_hci_cmd_received);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
42962306a36Sopenharmony_ci			     u8 state)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
43262306a36Sopenharmony_ci	int r, i;
43362306a36Sopenharmony_ci	struct sk_buff *sk_host_list;
43462306a36Sopenharmony_ci	u8 host_id;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	switch (se_idx) {
43762306a36Sopenharmony_ci	case ST_NCI_UICC_HOST_ID:
43862306a36Sopenharmony_ci		ndev->hci_dev->count_pipes = 0;
43962306a36Sopenharmony_ci		ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_UICC;
44062306a36Sopenharmony_ci		break;
44162306a36Sopenharmony_ci	case ST_NCI_ESE_HOST_ID:
44262306a36Sopenharmony_ci		ndev->hci_dev->count_pipes = 0;
44362306a36Sopenharmony_ci		ndev->hci_dev->expected_pipes = ST_NCI_SE_COUNT_PIPE_EMBEDDED;
44462306a36Sopenharmony_ci		break;
44562306a36Sopenharmony_ci	default:
44662306a36Sopenharmony_ci		return -EINVAL;
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	/*
45062306a36Sopenharmony_ci	 * Wait for an EVT_HOT_PLUG in order to
45162306a36Sopenharmony_ci	 * retrieve a relevant host list.
45262306a36Sopenharmony_ci	 */
45362306a36Sopenharmony_ci	reinit_completion(&info->se_info.req_completion);
45462306a36Sopenharmony_ci	r = nci_nfcee_mode_set(ndev, se_idx, state);
45562306a36Sopenharmony_ci	if (r != NCI_STATUS_OK)
45662306a36Sopenharmony_ci		return r;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	mod_timer(&info->se_info.se_active_timer, jiffies +
45962306a36Sopenharmony_ci		msecs_to_jiffies(ST_NCI_SE_TO_HOT_PLUG));
46062306a36Sopenharmony_ci	info->se_info.se_active = true;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	/* Ignore return value and check in any case the host_list */
46362306a36Sopenharmony_ci	wait_for_completion_interruptible(&info->se_info.req_completion);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/* There might be some "collision" after receiving a HOT_PLUG event
46662306a36Sopenharmony_ci	 * This may cause the CLF to not answer to the next hci command.
46762306a36Sopenharmony_ci	 * There is no possible synchronization to prevent this.
46862306a36Sopenharmony_ci	 * Adding a small delay is the only way to solve the issue.
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	if (info->se_info.se_status->is_ese_present &&
47162306a36Sopenharmony_ci	    info->se_info.se_status->is_uicc_present)
47262306a36Sopenharmony_ci		usleep_range(15000, 20000);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
47562306a36Sopenharmony_ci			NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
47662306a36Sopenharmony_ci	if (r != NCI_HCI_ANY_OK)
47762306a36Sopenharmony_ci		return r;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	for (i = 0; i < sk_host_list->len &&
48062306a36Sopenharmony_ci		sk_host_list->data[i] != se_idx; i++)
48162306a36Sopenharmony_ci		;
48262306a36Sopenharmony_ci	host_id = sk_host_list->data[i];
48362306a36Sopenharmony_ci	kfree_skb(sk_host_list);
48462306a36Sopenharmony_ci	if (state == ST_NCI_SE_MODE_ON && host_id == se_idx)
48562306a36Sopenharmony_ci		return se_idx;
48662306a36Sopenharmony_ci	else if (state == ST_NCI_SE_MODE_OFF && host_id != se_idx)
48762306a36Sopenharmony_ci		return se_idx;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	return -1;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ciint st_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	int r;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/*
49762306a36Sopenharmony_ci	 * According to upper layer, se_idx == NFC_SE_UICC when
49862306a36Sopenharmony_ci	 * info->se_info.se_status->is_uicc_enable is true should never happen
49962306a36Sopenharmony_ci	 * Same for eSE.
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_OFF);
50262306a36Sopenharmony_ci	if (r < 0) {
50362306a36Sopenharmony_ci		/* Do best effort to release SWP */
50462306a36Sopenharmony_ci		if (se_idx == NFC_SE_EMBEDDED) {
50562306a36Sopenharmony_ci			r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
50662306a36Sopenharmony_ci					ST_NCI_EVT_SE_END_OF_APDU_TRANSFER,
50762306a36Sopenharmony_ci					NULL, 0);
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci		return r;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	return 0;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_disable_se);
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ciint st_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	int r;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/*
52162306a36Sopenharmony_ci	 * According to upper layer, se_idx == NFC_SE_UICC when
52262306a36Sopenharmony_ci	 * info->se_info.se_status->is_uicc_enable is true should never happen.
52362306a36Sopenharmony_ci	 * Same for eSE.
52462306a36Sopenharmony_ci	 */
52562306a36Sopenharmony_ci	r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_ON);
52662306a36Sopenharmony_ci	if (r == ST_NCI_ESE_HOST_ID) {
52762306a36Sopenharmony_ci		st_nci_se_get_atr(ndev);
52862306a36Sopenharmony_ci		r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
52962306a36Sopenharmony_ci				ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (r < 0) {
53362306a36Sopenharmony_ci		/*
53462306a36Sopenharmony_ci		 * The activation procedure failed, the secure element
53562306a36Sopenharmony_ci		 * is not connected. Remove from the list.
53662306a36Sopenharmony_ci		 */
53762306a36Sopenharmony_ci		nfc_remove_se(ndev->nfc_dev, se_idx);
53862306a36Sopenharmony_ci		return r;
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_enable_se);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic int st_nci_hci_network_init(struct nci_dev *ndev)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
54862306a36Sopenharmony_ci	struct core_conn_create_dest_spec_params *dest_params;
54962306a36Sopenharmony_ci	struct dest_spec_params spec_params;
55062306a36Sopenharmony_ci	struct nci_conn_info    *conn_info;
55162306a36Sopenharmony_ci	int r, dev_num;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	dest_params =
55462306a36Sopenharmony_ci		kzalloc(sizeof(struct core_conn_create_dest_spec_params) +
55562306a36Sopenharmony_ci			sizeof(struct dest_spec_params), GFP_KERNEL);
55662306a36Sopenharmony_ci	if (dest_params == NULL)
55762306a36Sopenharmony_ci		return -ENOMEM;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	dest_params->type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE;
56062306a36Sopenharmony_ci	dest_params->length = sizeof(struct dest_spec_params);
56162306a36Sopenharmony_ci	spec_params.id = ndev->hci_dev->nfcee_id;
56262306a36Sopenharmony_ci	spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS;
56362306a36Sopenharmony_ci	memcpy(dest_params->value, &spec_params,
56462306a36Sopenharmony_ci	       sizeof(struct dest_spec_params));
56562306a36Sopenharmony_ci	r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1,
56662306a36Sopenharmony_ci				 sizeof(struct core_conn_create_dest_spec_params) +
56762306a36Sopenharmony_ci				 sizeof(struct dest_spec_params),
56862306a36Sopenharmony_ci				 dest_params);
56962306a36Sopenharmony_ci	if (r != NCI_STATUS_OK)
57062306a36Sopenharmony_ci		goto free_dest_params;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	conn_info = ndev->hci_dev->conn_info;
57362306a36Sopenharmony_ci	if (!conn_info)
57462306a36Sopenharmony_ci		goto free_dest_params;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	ndev->hci_dev->init_data.gate_count = ARRAY_SIZE(st_nci_gates);
57762306a36Sopenharmony_ci	memcpy(ndev->hci_dev->init_data.gates, st_nci_gates,
57862306a36Sopenharmony_ci	       sizeof(st_nci_gates));
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	/*
58162306a36Sopenharmony_ci	 * Session id must include the driver name + i2c bus addr
58262306a36Sopenharmony_ci	 * persistent info to discriminate 2 identical chips
58362306a36Sopenharmony_ci	 */
58462306a36Sopenharmony_ci	dev_num = find_first_zero_bit(dev_mask, ST_NCI_NUM_DEVICES);
58562306a36Sopenharmony_ci	if (dev_num >= ST_NCI_NUM_DEVICES) {
58662306a36Sopenharmony_ci		r = -ENODEV;
58762306a36Sopenharmony_ci		goto free_dest_params;
58862306a36Sopenharmony_ci	}
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	scnprintf(ndev->hci_dev->init_data.session_id,
59162306a36Sopenharmony_ci		  sizeof(ndev->hci_dev->init_data.session_id),
59262306a36Sopenharmony_ci		  "%s%2x", "ST21BH", dev_num);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	r = nci_hci_dev_session_init(ndev);
59562306a36Sopenharmony_ci	if (r != NCI_HCI_ANY_OK)
59662306a36Sopenharmony_ci		goto free_dest_params;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	/*
59962306a36Sopenharmony_ci	 * In factory mode, we prevent secure elements activation
60062306a36Sopenharmony_ci	 * by disabling nfcee on the current HCI connection id.
60162306a36Sopenharmony_ci	 * HCI will be used here only for proprietary commands.
60262306a36Sopenharmony_ci	 */
60362306a36Sopenharmony_ci	if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
60462306a36Sopenharmony_ci		r = nci_nfcee_mode_set(ndev,
60562306a36Sopenharmony_ci				       ndev->hci_dev->conn_info->dest_params->id,
60662306a36Sopenharmony_ci				       NCI_NFCEE_DISABLE);
60762306a36Sopenharmony_ci	else
60862306a36Sopenharmony_ci		r = nci_nfcee_mode_set(ndev,
60962306a36Sopenharmony_ci				       ndev->hci_dev->conn_info->dest_params->id,
61062306a36Sopenharmony_ci				       NCI_NFCEE_ENABLE);
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cifree_dest_params:
61362306a36Sopenharmony_ci	kfree(dest_params);
61462306a36Sopenharmony_ci	return r;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ciint st_nci_discover_se(struct nci_dev *ndev)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci	u8 white_list[2];
62062306a36Sopenharmony_ci	int r, wl_size = 0;
62162306a36Sopenharmony_ci	int se_count = 0;
62262306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	r = st_nci_hci_network_init(ndev);
62562306a36Sopenharmony_ci	if (r != 0)
62662306a36Sopenharmony_ci		return r;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
62962306a36Sopenharmony_ci		return 0;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (info->se_info.se_status->is_uicc_present)
63262306a36Sopenharmony_ci		white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
63362306a36Sopenharmony_ci	if (info->se_info.se_status->is_ese_present)
63462306a36Sopenharmony_ci		white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (wl_size) {
63762306a36Sopenharmony_ci		r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
63862306a36Sopenharmony_ci				      NCI_HCI_ADMIN_PARAM_WHITELIST,
63962306a36Sopenharmony_ci				      white_list, wl_size);
64062306a36Sopenharmony_ci		if (r != NCI_HCI_ANY_OK)
64162306a36Sopenharmony_ci			return r;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	if (info->se_info.se_status->is_uicc_present) {
64562306a36Sopenharmony_ci		nfc_add_se(ndev->nfc_dev, ST_NCI_UICC_HOST_ID, NFC_SE_UICC);
64662306a36Sopenharmony_ci		se_count++;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (info->se_info.se_status->is_ese_present) {
65062306a36Sopenharmony_ci		nfc_add_se(ndev->nfc_dev, ST_NCI_ESE_HOST_ID, NFC_SE_EMBEDDED);
65162306a36Sopenharmony_ci		se_count++;
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	return !se_count;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(st_nci_discover_se);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ciint st_nci_se_io(struct nci_dev *ndev, u32 se_idx,
65962306a36Sopenharmony_ci		       u8 *apdu, size_t apdu_length,
66062306a36Sopenharmony_ci		       se_io_cb_t cb, void *cb_context)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	switch (se_idx) {
66562306a36Sopenharmony_ci	case ST_NCI_ESE_HOST_ID:
66662306a36Sopenharmony_ci		info->se_info.cb = cb;
66762306a36Sopenharmony_ci		info->se_info.cb_context = cb_context;
66862306a36Sopenharmony_ci		mod_timer(&info->se_info.bwi_timer, jiffies +
66962306a36Sopenharmony_ci			  msecs_to_jiffies(info->se_info.wt_timeout));
67062306a36Sopenharmony_ci		info->se_info.bwi_active = true;
67162306a36Sopenharmony_ci		return nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
67262306a36Sopenharmony_ci					ST_NCI_EVT_TRANSMIT_DATA, apdu,
67362306a36Sopenharmony_ci					apdu_length);
67462306a36Sopenharmony_ci	default:
67562306a36Sopenharmony_ci		/* Need to free cb_context here as at the moment we can't
67662306a36Sopenharmony_ci		 * clearly indicate to the caller if the callback function
67762306a36Sopenharmony_ci		 * would be called (and free it) or not. In both cases a
67862306a36Sopenharmony_ci		 * negative value may be returned to the caller.
67962306a36Sopenharmony_ci		 */
68062306a36Sopenharmony_ci		kfree(cb_context);
68162306a36Sopenharmony_ci		return -ENODEV;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ciEXPORT_SYMBOL(st_nci_se_io);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic void st_nci_se_wt_timeout(struct timer_list *t)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	/*
68962306a36Sopenharmony_ci	 * No answer from the secure element
69062306a36Sopenharmony_ci	 * within the defined timeout.
69162306a36Sopenharmony_ci	 * Let's send a reset request as recovery procedure.
69262306a36Sopenharmony_ci	 * According to the situation, we first try to send a software reset
69362306a36Sopenharmony_ci	 * to the secure element. If the next command is still not
69462306a36Sopenharmony_ci	 * answering in time, we send to the CLF a secure element hardware
69562306a36Sopenharmony_ci	 * reset request.
69662306a36Sopenharmony_ci	 */
69762306a36Sopenharmony_ci	/* hardware reset managed through VCC_UICC_OUT power supply */
69862306a36Sopenharmony_ci	u8 param = 0x01;
69962306a36Sopenharmony_ci	struct st_nci_info *info = from_timer(info, t, se_info.bwi_timer);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	info->se_info.bwi_active = false;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	if (!info->se_info.xch_error) {
70462306a36Sopenharmony_ci		info->se_info.xch_error = true;
70562306a36Sopenharmony_ci		nci_hci_send_event(info->ndlc->ndev, ST_NCI_APDU_READER_GATE,
70662306a36Sopenharmony_ci				ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
70762306a36Sopenharmony_ci	} else {
70862306a36Sopenharmony_ci		info->se_info.xch_error = false;
70962306a36Sopenharmony_ci		nci_hci_send_event(info->ndlc->ndev, ST_NCI_DEVICE_MGNT_GATE,
71062306a36Sopenharmony_ci				ST_NCI_EVT_SE_HARD_RESET, &param, 1);
71162306a36Sopenharmony_ci	}
71262306a36Sopenharmony_ci	info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void st_nci_se_activation_timeout(struct timer_list *t)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct st_nci_info *info = from_timer(info, t,
71862306a36Sopenharmony_ci					      se_info.se_active_timer);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	info->se_info.se_active = false;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	complete(&info->se_info.req_completion);
72362306a36Sopenharmony_ci}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ciint st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	init_completion(&info->se_info.req_completion);
73062306a36Sopenharmony_ci	/* initialize timers */
73162306a36Sopenharmony_ci	timer_setup(&info->se_info.bwi_timer, st_nci_se_wt_timeout, 0);
73262306a36Sopenharmony_ci	info->se_info.bwi_active = false;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	timer_setup(&info->se_info.se_active_timer,
73562306a36Sopenharmony_ci		    st_nci_se_activation_timeout, 0);
73662306a36Sopenharmony_ci	info->se_info.se_active = false;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	info->se_info.xch_error = false;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	info->se_info.wt_timeout =
74162306a36Sopenharmony_ci		ST_NCI_BWI_TO_TIMEOUT(ST_NCI_ATR_DEFAULT_BWI);
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	info->se_info.se_status = se_status;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ciEXPORT_SYMBOL(st_nci_se_init);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_civoid st_nci_se_deinit(struct nci_dev *ndev)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct st_nci_info *info = nci_get_drvdata(ndev);
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	if (info->se_info.bwi_active)
75462306a36Sopenharmony_ci		del_timer_sync(&info->se_info.bwi_timer);
75562306a36Sopenharmony_ci	if (info->se_info.se_active)
75662306a36Sopenharmony_ci		del_timer_sync(&info->se_info.se_active_timer);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	info->se_info.se_active = false;
75962306a36Sopenharmony_ci	info->se_info.bwi_active = false;
76062306a36Sopenharmony_ci}
76162306a36Sopenharmony_ciEXPORT_SYMBOL(st_nci_se_deinit);
76262306a36Sopenharmony_ci
763