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, ¶m, 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