2130fb97fe
st21nfca has 1 physical SWP line and can support up to 2 secure elements (UICC & eSE) thanks to an external switch managed with a gpio. The platform integrator needs to specify thanks to 2 initialization properties, uicc-present and ese-present, if it is suppose to have uicc and/or ese. Of course if the platform does not have an external switch, only one kind of secure element can be supported. Those parameters are under platform integrator responsibilities. During initialization, the white_list will be set according to those parameters. The discovery_se function will assume a secure element is physically present according to uicc-present and ese-present values and will add it to the secure element list. On ese activation, the atr is retrieved to calculate a command exchange timeout based on the first atr(TB) value. The se_io will allow to transfer data over SWP. 2 kind of events may appear after a data is sent over: - ST21NFCA_EVT_TRANSMIT_DATA when receiving an apdu answer - ST21NFCA_EVT_WTX_REQUEST when the secure element needs more time than expected to compute a command. If this timeout expired, a first recovery tentative consist to send a simple software reset proprietary command. If this tentative still fail, a second recovery tentative consist to send a hardware reset proprietary command. This function is only relevant for eSE like secure element. This patch also change the way a pipe is referenced. There can be different pipe connected to the same gate with different host destination (ex: CONNECTIVITY). In order to keep host information every pipe are reference with a tuple (gate, host). In order to reduce changes, we are keeping unchanged the way a gate is addressed on the Terminal Host. However, this is working because we consider the apdu reader gate is only present on the eSE slot also the connectivity gate cannot give a reliable value; it will give the latest stored pipe value. Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
1063 lines
28 KiB
C
1063 lines
28 KiB
C
/*
|
|
* HCI based Driver for STMicroelectronics NFC Chip
|
|
*
|
|
* Copyright (C) 2014 STMicroelectronics SAS. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/nfc.h>
|
|
#include <net/nfc/hci.h>
|
|
#include <net/nfc/llc.h>
|
|
|
|
#include "st21nfca.h"
|
|
#include "st21nfca_dep.h"
|
|
#include "st21nfca_se.h"
|
|
|
|
#define DRIVER_DESC "HCI NFC driver for ST21NFCA"
|
|
|
|
#define FULL_VERSION_LEN 3
|
|
|
|
/* Proprietary gates, events, commands and registers */
|
|
|
|
/* Commands that apply to all RF readers */
|
|
#define ST21NFCA_RF_READER_CMD_PRESENCE_CHECK 0x30
|
|
|
|
#define ST21NFCA_RF_READER_ISO15693_GATE 0x12
|
|
#define ST21NFCA_RF_READER_ISO15693_INVENTORY 0x01
|
|
|
|
/*
|
|
* Reader gate for communication with contact-less cards using Type A
|
|
* protocol ISO14443-3 but not compliant with ISO14443-4
|
|
*/
|
|
#define ST21NFCA_RF_READER_14443_3_A_GATE 0x15
|
|
#define ST21NFCA_RF_READER_14443_3_A_UID 0x02
|
|
#define ST21NFCA_RF_READER_14443_3_A_ATQA 0x03
|
|
#define ST21NFCA_RF_READER_14443_3_A_SAK 0x04
|
|
|
|
#define ST21NFCA_RF_READER_F_DATARATE 0x01
|
|
#define ST21NFCA_RF_READER_F_DATARATE_106 0x01
|
|
#define ST21NFCA_RF_READER_F_DATARATE_212 0x02
|
|
#define ST21NFCA_RF_READER_F_DATARATE_424 0x04
|
|
#define ST21NFCA_RF_READER_F_POL_REQ 0x02
|
|
#define ST21NFCA_RF_READER_F_POL_REQ_DEFAULT 0xffff0000
|
|
#define ST21NFCA_RF_READER_F_NFCID2 0x03
|
|
#define ST21NFCA_RF_READER_F_NFCID1 0x04
|
|
|
|
#define ST21NFCA_RF_CARD_F_MODE 0x01
|
|
#define ST21NFCA_RF_CARD_F_NFCID2_LIST 0x04
|
|
#define ST21NFCA_RF_CARD_F_NFCID1 0x05
|
|
#define ST21NFCA_RF_CARD_F_SENS_RES 0x06
|
|
#define ST21NFCA_RF_CARD_F_SEL_RES 0x07
|
|
#define ST21NFCA_RF_CARD_F_DATARATE 0x08
|
|
#define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01
|
|
|
|
#define ST21NFCA_DEVICE_MGNT_PIPE 0x02
|
|
|
|
#define ST21NFCA_DM_GETINFO 0x13
|
|
#define ST21NFCA_DM_GETINFO_PIPE_LIST 0x02
|
|
#define ST21NFCA_DM_GETINFO_PIPE_INFO 0x01
|
|
#define ST21NFCA_DM_PIPE_CREATED 0x02
|
|
#define ST21NFCA_DM_PIPE_OPEN 0x04
|
|
#define ST21NFCA_DM_RF_ACTIVE 0x80
|
|
#define ST21NFCA_DM_DISCONNECT 0x30
|
|
|
|
#define ST21NFCA_DM_IS_PIPE_OPEN(p) \
|
|
((p & 0x0f) == (ST21NFCA_DM_PIPE_CREATED | ST21NFCA_DM_PIPE_OPEN))
|
|
|
|
#define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/
|
|
|
|
#define ST21NFCA_EVT_HOT_PLUG 0x03
|
|
#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
|
|
|
|
#define ST21NFCA_SE_TO_PIPES 2000
|
|
|
|
static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES);
|
|
|
|
static struct nfc_hci_gate st21nfca_gates[] = {
|
|
{NFC_HCI_ADMIN_GATE, NFC_HCI_ADMIN_PIPE},
|
|
{NFC_HCI_LOOPBACK_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_ID_MGMT_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_LINK_MGMT_GATE, NFC_HCI_LINK_MGMT_PIPE},
|
|
{NFC_HCI_RF_READER_B_GATE, NFC_HCI_INVALID_PIPE},
|
|
{NFC_HCI_RF_READER_A_GATE, NFC_HCI_INVALID_PIPE},
|
|
{ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE},
|
|
{ST21NFCA_RF_READER_F_GATE, NFC_HCI_INVALID_PIPE},
|
|
{ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE},
|
|
{ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE},
|
|
{ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE},
|
|
|
|
/* Secure element pipes are created by secure element host */
|
|
{ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
|
|
{ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE},
|
|
};
|
|
|
|
struct st21nfca_pipe_info {
|
|
u8 pipe_state;
|
|
u8 src_host_id;
|
|
u8 src_gate_id;
|
|
u8 dst_host_id;
|
|
u8 dst_gate_id;
|
|
} __packed;
|
|
|
|
/* Largest headroom needed for outgoing custom commands */
|
|
#define ST21NFCA_CMDS_HEADROOM 7
|
|
|
|
static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev)
|
|
{
|
|
int i, j, r;
|
|
struct sk_buff *skb_pipe_list, *skb_pipe_info;
|
|
struct st21nfca_pipe_info *info;
|
|
|
|
u8 pipe_list[] = { ST21NFCA_DM_GETINFO_PIPE_LIST,
|
|
NFC_HCI_TERMINAL_HOST_ID
|
|
};
|
|
u8 pipe_info[] = { ST21NFCA_DM_GETINFO_PIPE_INFO,
|
|
NFC_HCI_TERMINAL_HOST_ID, 0
|
|
};
|
|
|
|
/* On ST21NFCA device pipes number are dynamics
|
|
* A maximum of 16 pipes can be created at the same time
|
|
* If pipes are already created, hci_dev_up will fail.
|
|
* Doing a clear all pipe is a bad idea because:
|
|
* - It does useless EEPROM cycling
|
|
* - It might cause issue for secure elements support
|
|
* (such as removing connectivity or APDU reader pipe)
|
|
* A better approach on ST21NFCA is to:
|
|
* - get a pipe list for each host.
|
|
* (eg: NFC_HCI_HOST_CONTROLLER_ID for now).
|
|
* (TODO Later on UICC HOST and eSE HOST)
|
|
* - get pipe information
|
|
* - match retrieved pipe list in st21nfca_gates
|
|
* ST21NFCA_DEVICE_MGNT_GATE is a proprietary gate
|
|
* with ST21NFCA_DEVICE_MGNT_PIPE.
|
|
* Pipe can be closed and need to be open.
|
|
*/
|
|
r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID,
|
|
ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_DEVICE_MGNT_PIPE);
|
|
if (r < 0)
|
|
goto free_info;
|
|
|
|
/* Get pipe list */
|
|
r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_DM_GETINFO, pipe_list, sizeof(pipe_list),
|
|
&skb_pipe_list);
|
|
if (r < 0)
|
|
goto free_info;
|
|
|
|
/* Complete the existing gate_pipe table */
|
|
for (i = 0; i < skb_pipe_list->len; i++) {
|
|
pipe_info[2] = skb_pipe_list->data[i];
|
|
r = nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_DM_GETINFO, pipe_info,
|
|
sizeof(pipe_info), &skb_pipe_info);
|
|
|
|
if (r)
|
|
continue;
|
|
|
|
/*
|
|
* Match pipe ID and gate ID
|
|
* Output format from ST21NFC_DM_GETINFO is:
|
|
* - pipe state (1byte)
|
|
* - source hid (1byte)
|
|
* - source gid (1byte)
|
|
* - destination hid (1byte)
|
|
* - destination gid (1byte)
|
|
*/
|
|
info = (struct st21nfca_pipe_info *) skb_pipe_info->data;
|
|
if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE &&
|
|
info->src_host_id != ST21NFCA_ESE_HOST_ID) {
|
|
pr_err("Unexpected apdu_reader pipe on host %x\n",
|
|
info->src_host_id);
|
|
continue;
|
|
}
|
|
|
|
for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) &&
|
|
(st21nfca_gates[j].gate != info->dst_gate_id) ; j++)
|
|
;
|
|
|
|
if (j < ARRAY_SIZE(st21nfca_gates) &&
|
|
st21nfca_gates[j].gate == info->dst_gate_id &&
|
|
ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) {
|
|
st21nfca_gates[j].pipe = pipe_info[2];
|
|
|
|
hdev->gate2pipe[st21nfca_gates[j].gate] =
|
|
st21nfca_gates[j].pipe;
|
|
hdev->pipes[st21nfca_gates[j].pipe].gate =
|
|
st21nfca_gates[j].gate;
|
|
hdev->pipes[st21nfca_gates[j].pipe].dest_host =
|
|
info->src_host_id;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 3 gates have a well known pipe ID.
|
|
* They will never appear in the pipe list
|
|
*/
|
|
if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) {
|
|
for (i = skb_pipe_list->len + 3;
|
|
i < ARRAY_SIZE(st21nfca_gates) - 2; i++) {
|
|
r = nfc_hci_connect_gate(hdev,
|
|
NFC_HCI_HOST_CONTROLLER_ID,
|
|
st21nfca_gates[i].gate,
|
|
st21nfca_gates[i].pipe);
|
|
if (r < 0)
|
|
goto free_info;
|
|
}
|
|
}
|
|
|
|
memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
|
|
free_info:
|
|
kfree_skb(skb_pipe_info);
|
|
kfree_skb(skb_pipe_list);
|
|
return r;
|
|
}
|
|
|
|
static int st21nfca_hci_open(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
int r;
|
|
|
|
mutex_lock(&info->info_lock);
|
|
|
|
if (info->state != ST21NFCA_ST_COLD) {
|
|
r = -EBUSY;
|
|
goto out;
|
|
}
|
|
|
|
r = info->phy_ops->enable(info->phy_id);
|
|
|
|
if (r == 0)
|
|
info->state = ST21NFCA_ST_READY;
|
|
|
|
out:
|
|
mutex_unlock(&info->info_lock);
|
|
return r;
|
|
}
|
|
|
|
static void st21nfca_hci_close(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
mutex_lock(&info->info_lock);
|
|
|
|
if (info->state == ST21NFCA_ST_COLD)
|
|
goto out;
|
|
|
|
info->phy_ops->disable(info->phy_id);
|
|
info->state = ST21NFCA_ST_COLD;
|
|
|
|
out:
|
|
mutex_unlock(&info->info_lock);
|
|
}
|
|
|
|
static int st21nfca_hci_ready(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
struct sk_buff *skb;
|
|
|
|
u8 param;
|
|
u8 white_list[2];
|
|
int wl_size = 0;
|
|
int r;
|
|
|
|
if (info->se_status->is_ese_present &&
|
|
info->se_status->is_uicc_present) {
|
|
white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
|
|
white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
|
|
} else if (!info->se_status->is_ese_present &&
|
|
info->se_status->is_uicc_present) {
|
|
white_list[wl_size++] = NFC_HCI_UICC_HOST_ID;
|
|
} else if (info->se_status->is_ese_present &&
|
|
!info->se_status->is_uicc_present) {
|
|
white_list[wl_size++] = ST21NFCA_ESE_HOST_ID;
|
|
}
|
|
|
|
if (wl_size) {
|
|
r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE,
|
|
NFC_HCI_ADMIN_WHITELIST,
|
|
(u8 *) &white_list, wl_size);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
/* Set NFC_MODE in device management gate to enable */
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_NFC_MODE, &skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param = skb->data[0];
|
|
kfree_skb(skb);
|
|
if (param == 0) {
|
|
param = 1;
|
|
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_NFC_MODE, ¶m, 1);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE,
|
|
NFC_HCI_ID_MGMT_VERSION_SW, &skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (skb->len != FULL_VERSION_LEN) {
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
|
|
print_hex_dump(KERN_DEBUG, "FULL VERSION SOFTWARE INFO: ",
|
|
DUMP_PREFIX_NONE, 16, 1,
|
|
skb->data, FULL_VERSION_LEN, false);
|
|
|
|
kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int st21nfca_hci_xmit(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
return info->phy_ops->write(info->phy_id, skb);
|
|
}
|
|
|
|
static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev,
|
|
u32 im_protocols, u32 tm_protocols)
|
|
{
|
|
int r;
|
|
u32 pol_req;
|
|
u8 param[19];
|
|
struct sk_buff *datarate_skb;
|
|
|
|
pr_info(DRIVER_DESC ": %s protocols 0x%x 0x%x\n",
|
|
__func__, im_protocols, tm_protocols);
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
if (im_protocols) {
|
|
/*
|
|
* enable polling according to im_protocols & tm_protocols
|
|
* - CLOSE pipe according to im_protocols & tm_protocols
|
|
*/
|
|
if ((NFC_HCI_RF_READER_B_GATE & im_protocols) == 0) {
|
|
r = nfc_hci_disconnect_gate(hdev,
|
|
NFC_HCI_RF_READER_B_GATE);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if ((NFC_HCI_RF_READER_A_GATE & im_protocols) == 0) {
|
|
r = nfc_hci_disconnect_gate(hdev,
|
|
NFC_HCI_RF_READER_A_GATE);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if ((ST21NFCA_RF_READER_F_GATE & im_protocols) == 0) {
|
|
r = nfc_hci_disconnect_gate(hdev,
|
|
ST21NFCA_RF_READER_F_GATE);
|
|
if (r < 0)
|
|
return r;
|
|
} else {
|
|
hdev->gb = nfc_get_local_general_bytes(hdev->ndev,
|
|
&hdev->gb_len);
|
|
|
|
if (hdev->gb == NULL || hdev->gb_len == 0) {
|
|
im_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
|
tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK;
|
|
}
|
|
|
|
param[0] = ST21NFCA_RF_READER_F_DATARATE_106 |
|
|
ST21NFCA_RF_READER_F_DATARATE_212 |
|
|
ST21NFCA_RF_READER_F_DATARATE_424;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
|
ST21NFCA_RF_READER_F_DATARATE,
|
|
param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
pol_req = be32_to_cpu((__force __be32)
|
|
ST21NFCA_RF_READER_F_POL_REQ_DEFAULT);
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
|
ST21NFCA_RF_READER_F_POL_REQ,
|
|
(u8 *) &pol_req, 4);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if ((ST21NFCA_RF_READER_14443_3_A_GATE & im_protocols) == 0) {
|
|
r = nfc_hci_disconnect_gate(hdev,
|
|
ST21NFCA_RF_READER_14443_3_A_GATE);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if ((ST21NFCA_RF_READER_ISO15693_GATE & im_protocols) == 0) {
|
|
r = nfc_hci_disconnect_gate(hdev,
|
|
ST21NFCA_RF_READER_ISO15693_GATE);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
r = nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_READER_REQUESTED, NULL, 0);
|
|
if (r < 0)
|
|
nfc_hci_send_event(hdev, NFC_HCI_RF_READER_A_GATE,
|
|
NFC_HCI_EVT_END_OPERATION, NULL, 0);
|
|
}
|
|
|
|
if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_DATARATE,
|
|
&datarate_skb);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* Configure the maximum supported datarate to 424Kbps */
|
|
if (datarate_skb->len > 0 &&
|
|
datarate_skb->data[0] !=
|
|
ST21NFCA_RF_CARD_F_DATARATE_212_424) {
|
|
param[0] = ST21NFCA_RF_CARD_F_DATARATE_212_424;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_DATARATE,
|
|
param, 1);
|
|
if (r < 0) {
|
|
kfree_skb(datarate_skb);
|
|
return r;
|
|
}
|
|
}
|
|
kfree_skb(datarate_skb);
|
|
|
|
/*
|
|
* Configure sens_res
|
|
*
|
|
* NFC Forum Digital Spec Table 7:
|
|
* NFCID1 size: triple (10 bytes)
|
|
*/
|
|
param[0] = 0x00;
|
|
param[1] = 0x08;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_SENS_RES, param, 2);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/*
|
|
* Configure sel_res
|
|
*
|
|
* NFC Forum Digistal Spec Table 17:
|
|
* b3 set to 0b (value b7-b6):
|
|
* - 10b: Configured for NFC-DEP Protocol
|
|
*/
|
|
param[0] = 0x40;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_SEL_RES, param, 1);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* Configure NFCID1 Random uid */
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_NFCID1, NULL, 0);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
/* Configure NFCID2_LIST */
|
|
/* System Code */
|
|
param[0] = 0x00;
|
|
param[1] = 0x00;
|
|
/* NFCID2 */
|
|
param[2] = 0x01;
|
|
param[3] = 0xfe;
|
|
param[4] = 'S';
|
|
param[5] = 'T';
|
|
param[6] = 'M';
|
|
param[7] = 'i';
|
|
param[8] = 'c';
|
|
param[9] = 'r';
|
|
/* 8 byte Pad bytes used for polling respone frame */
|
|
|
|
/*
|
|
* Configuration byte:
|
|
* - bit 0: define the default NFCID2 entry used when the
|
|
* system code is equal to 'FFFF'
|
|
* - bit 1: use a random value for lowest 6 bytes of
|
|
* NFCID2 value
|
|
* - bit 2: ignore polling request frame if request code
|
|
* is equal to '01'
|
|
* - Other bits are RFU
|
|
*/
|
|
param[18] = 0x01;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_NFCID2_LIST, param,
|
|
19);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
param[0] = 0x02;
|
|
r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE,
|
|
ST21NFCA_RF_CARD_F_MODE, param, 1);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static void st21nfca_hci_stop_poll(struct nfc_hci_dev *hdev)
|
|
{
|
|
nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_DM_DISCONNECT, NULL, 0, NULL);
|
|
}
|
|
|
|
static int st21nfca_get_iso14443_3_atqa(struct nfc_hci_dev *hdev, u16 *atqa)
|
|
{
|
|
int r;
|
|
struct sk_buff *atqa_skb = NULL;
|
|
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
|
|
ST21NFCA_RF_READER_14443_3_A_ATQA, &atqa_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
if (atqa_skb->len != 2) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
|
|
*atqa = be16_to_cpu(*(__be16 *) atqa_skb->data);
|
|
|
|
exit:
|
|
kfree_skb(atqa_skb);
|
|
return r;
|
|
}
|
|
|
|
static int st21nfca_get_iso14443_3_sak(struct nfc_hci_dev *hdev, u8 *sak)
|
|
{
|
|
int r;
|
|
struct sk_buff *sak_skb = NULL;
|
|
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
|
|
ST21NFCA_RF_READER_14443_3_A_SAK, &sak_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
if (sak_skb->len != 1) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
|
|
*sak = sak_skb->data[0];
|
|
|
|
exit:
|
|
kfree_skb(sak_skb);
|
|
return r;
|
|
}
|
|
|
|
static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate,
|
|
int *len)
|
|
{
|
|
int r;
|
|
struct sk_buff *uid_skb = NULL;
|
|
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_14443_3_A_GATE,
|
|
ST21NFCA_RF_READER_14443_3_A_UID, &uid_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
if (uid_skb->len == 0 || uid_skb->len > NFC_NFCID1_MAXSIZE) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
|
|
gate = uid_skb->data;
|
|
*len = uid_skb->len;
|
|
exit:
|
|
kfree_skb(uid_skb);
|
|
return r;
|
|
}
|
|
|
|
static int st21nfca_get_iso15693_inventory(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target)
|
|
{
|
|
int r;
|
|
struct sk_buff *inventory_skb = NULL;
|
|
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_ISO15693_GATE,
|
|
ST21NFCA_RF_READER_ISO15693_INVENTORY,
|
|
&inventory_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
skb_pull(inventory_skb, 2);
|
|
|
|
if (inventory_skb->len == 0 ||
|
|
inventory_skb->len > NFC_ISO15693_UID_MAXSIZE) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(target->iso15693_uid, inventory_skb->data, inventory_skb->len);
|
|
target->iso15693_dsfid = inventory_skb->data[1];
|
|
target->is_iso15693 = 1;
|
|
exit:
|
|
kfree_skb(inventory_skb);
|
|
return r;
|
|
}
|
|
|
|
static int st21nfca_hci_dep_link_up(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target, u8 comm_mode,
|
|
u8 *gb, size_t gb_len)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
info->dep_info.idx = target->idx;
|
|
return st21nfca_im_send_atr_req(hdev, gb, gb_len);
|
|
}
|
|
|
|
static int st21nfca_hci_dep_link_down(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
info->state = ST21NFCA_ST_READY;
|
|
|
|
return nfc_hci_send_cmd(hdev, ST21NFCA_DEVICE_MGNT_GATE,
|
|
ST21NFCA_DM_DISCONNECT, NULL, 0, NULL);
|
|
}
|
|
|
|
static int st21nfca_hci_target_from_gate(struct nfc_hci_dev *hdev, u8 gate,
|
|
struct nfc_target *target)
|
|
{
|
|
int r, len;
|
|
u16 atqa;
|
|
u8 sak;
|
|
u8 uid[NFC_NFCID1_MAXSIZE];
|
|
|
|
switch (gate) {
|
|
case ST21NFCA_RF_READER_F_GATE:
|
|
target->supported_protocols = NFC_PROTO_FELICA_MASK;
|
|
break;
|
|
case ST21NFCA_RF_READER_14443_3_A_GATE:
|
|
/* ISO14443-3 type 1 or 2 tags */
|
|
r = st21nfca_get_iso14443_3_atqa(hdev, &atqa);
|
|
if (r < 0)
|
|
return r;
|
|
if (atqa == 0x000c) {
|
|
target->supported_protocols = NFC_PROTO_JEWEL_MASK;
|
|
target->sens_res = 0x0c00;
|
|
} else {
|
|
r = st21nfca_get_iso14443_3_sak(hdev, &sak);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
r = st21nfca_get_iso14443_3_uid(hdev, uid, &len);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
target->supported_protocols =
|
|
nfc_hci_sak_to_protocol(sak);
|
|
if (target->supported_protocols == 0xffffffff)
|
|
return -EPROTO;
|
|
|
|
target->sens_res = atqa;
|
|
target->sel_res = sak;
|
|
memcpy(target->nfcid1, uid, len);
|
|
target->nfcid1_len = len;
|
|
}
|
|
|
|
break;
|
|
case ST21NFCA_RF_READER_ISO15693_GATE:
|
|
target->supported_protocols = NFC_PROTO_ISO15693_MASK;
|
|
r = st21nfca_get_iso15693_inventory(hdev, target);
|
|
if (r < 0)
|
|
return r;
|
|
break;
|
|
default:
|
|
return -EPROTO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev,
|
|
u8 gate,
|
|
struct nfc_target *target)
|
|
{
|
|
int r;
|
|
struct sk_buff *nfcid_skb = NULL;
|
|
|
|
if (gate == ST21NFCA_RF_READER_F_GATE) {
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
|
ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
|
|
/*
|
|
* - After the recepton of polling response for type F frame
|
|
* at 212 or 424 Kbit/s, NFCID2 registry parameters will be
|
|
* updated.
|
|
* - After the reception of SEL_RES with NFCIP-1 compliant bit
|
|
* set for type A frame NFCID1 will be updated
|
|
*/
|
|
if (nfcid_skb->len > 0) {
|
|
/* P2P in type F */
|
|
memcpy(target->sensf_res, nfcid_skb->data,
|
|
nfcid_skb->len);
|
|
target->sensf_res_len = nfcid_skb->len;
|
|
/* NFC Forum Digital Protocol Table 44 */
|
|
if (target->sensf_res[0] == 0x01 &&
|
|
target->sensf_res[1] == 0xfe)
|
|
target->supported_protocols =
|
|
NFC_PROTO_NFC_DEP_MASK;
|
|
else
|
|
target->supported_protocols =
|
|
NFC_PROTO_FELICA_MASK;
|
|
} else {
|
|
kfree_skb(nfcid_skb);
|
|
/* P2P in type A */
|
|
r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE,
|
|
ST21NFCA_RF_READER_F_NFCID1,
|
|
&nfcid_skb);
|
|
if (r < 0)
|
|
goto exit;
|
|
|
|
if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) {
|
|
r = -EPROTO;
|
|
goto exit;
|
|
}
|
|
memcpy(target->sensf_res, nfcid_skb->data,
|
|
nfcid_skb->len);
|
|
target->sensf_res_len = nfcid_skb->len;
|
|
target->supported_protocols = NFC_PROTO_NFC_DEP_MASK;
|
|
}
|
|
target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE;
|
|
}
|
|
r = 1;
|
|
exit:
|
|
kfree_skb(nfcid_skb);
|
|
return r;
|
|
}
|
|
|
|
#define ST21NFCA_CB_TYPE_READER_ISO15693 1
|
|
static void st21nfca_hci_data_exchange_cb(void *context, struct sk_buff *skb,
|
|
int err)
|
|
{
|
|
struct st21nfca_hci_info *info = context;
|
|
|
|
switch (info->async_cb_type) {
|
|
case ST21NFCA_CB_TYPE_READER_ISO15693:
|
|
if (err == 0)
|
|
skb_trim(skb, skb->len - 1);
|
|
info->async_cb(info->async_cb_context, skb, err);
|
|
break;
|
|
default:
|
|
if (err == 0)
|
|
kfree_skb(skb);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns:
|
|
* <= 0: driver handled the data exchange
|
|
* 1: driver doesn't especially handle, please do standard processing
|
|
*/
|
|
static int st21nfca_hci_im_transceive(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target,
|
|
struct sk_buff *skb,
|
|
data_exchange_cb_t cb, void *cb_context)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
pr_info(DRIVER_DESC ": %s for gate=%d len=%d\n", __func__,
|
|
target->hci_reader_gate, skb->len);
|
|
|
|
switch (target->hci_reader_gate) {
|
|
case ST21NFCA_RF_READER_F_GATE:
|
|
if (target->supported_protocols == NFC_PROTO_NFC_DEP_MASK)
|
|
return st21nfca_im_send_dep_req(hdev, skb);
|
|
|
|
*skb_push(skb, 1) = 0x1a;
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
|
ST21NFCA_WR_XCHG_DATA, skb->data,
|
|
skb->len, cb, cb_context);
|
|
case ST21NFCA_RF_READER_14443_3_A_GATE:
|
|
*skb_push(skb, 1) = 0x1a; /* CTR, see spec:10.2.2.1 */
|
|
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
|
ST21NFCA_WR_XCHG_DATA, skb->data,
|
|
skb->len, cb, cb_context);
|
|
case ST21NFCA_RF_READER_ISO15693_GATE:
|
|
info->async_cb_type = ST21NFCA_CB_TYPE_READER_ISO15693;
|
|
info->async_cb = cb;
|
|
info->async_cb_context = cb_context;
|
|
|
|
*skb_push(skb, 1) = 0x17;
|
|
|
|
return nfc_hci_send_cmd_async(hdev, target->hci_reader_gate,
|
|
ST21NFCA_WR_XCHG_DATA, skb->data,
|
|
skb->len,
|
|
st21nfca_hci_data_exchange_cb,
|
|
info);
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int st21nfca_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb)
|
|
{
|
|
return st21nfca_tm_send_dep_res(hdev, skb);
|
|
}
|
|
|
|
static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev,
|
|
struct nfc_target *target)
|
|
{
|
|
u8 fwi = 0x11;
|
|
|
|
switch (target->hci_reader_gate) {
|
|
case NFC_HCI_RF_READER_A_GATE:
|
|
case NFC_HCI_RF_READER_B_GATE:
|
|
/*
|
|
* PRESENCE_CHECK on those gates is available
|
|
* However, the answer to this command is taking 3 * fwi
|
|
* if the card is no present.
|
|
* Instead, we send an empty I-Frame with a very short
|
|
* configurable fwi ~604µs.
|
|
*/
|
|
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
|
ST21NFCA_WR_XCHG_DATA, &fwi, 1, NULL);
|
|
case ST21NFCA_RF_READER_14443_3_A_GATE:
|
|
return nfc_hci_send_cmd(hdev, target->hci_reader_gate,
|
|
ST21NFCA_RF_READER_CMD_PRESENCE_CHECK,
|
|
NULL, 0, NULL);
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
u8 gate = hdev->pipes[pipe].gate;
|
|
|
|
pr_debug("cmd: %x\n", cmd);
|
|
|
|
switch (cmd) {
|
|
case NFC_HCI_ANY_OPEN_PIPE:
|
|
if (gate != ST21NFCA_APDU_READER_GATE &&
|
|
hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID)
|
|
info->se_info.count_pipes++;
|
|
|
|
if (info->se_info.count_pipes == info->se_info.expected_pipes) {
|
|
del_timer_sync(&info->se_info.se_active_timer);
|
|
info->se_info.se_active = false;
|
|
info->se_info.count_pipes = 0;
|
|
complete(&info->se_info.req_completion);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event,
|
|
struct sk_buff *skb)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
pr_debug("admin event: %x\n", event);
|
|
|
|
switch (event) {
|
|
case ST21NFCA_EVT_HOT_PLUG:
|
|
if (info->se_info.se_active) {
|
|
if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) {
|
|
del_timer_sync(&info->se_info.se_active_timer);
|
|
info->se_info.se_active = false;
|
|
complete(&info->se_info.req_completion);
|
|
} else {
|
|
mod_timer(&info->se_info.se_active_timer,
|
|
jiffies +
|
|
msecs_to_jiffies(ST21NFCA_SE_TO_PIPES));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Returns:
|
|
* <= 0: driver handled the event, skb consumed
|
|
* 1: driver does not handle the event, please do standard processing
|
|
*/
|
|
static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe,
|
|
u8 event, struct sk_buff *skb)
|
|
{
|
|
u8 gate = hdev->pipes[pipe].gate;
|
|
u8 host = hdev->pipes[pipe].dest_host;
|
|
|
|
pr_debug("hci event: %d gate: %x\n", event, gate);
|
|
|
|
switch (gate) {
|
|
case NFC_HCI_ADMIN_GATE:
|
|
return st21nfca_admin_event_received(hdev, event, skb);
|
|
case ST21NFCA_RF_CARD_F_GATE:
|
|
return st21nfca_dep_event_received(hdev, event, skb);
|
|
case ST21NFCA_CONNECTIVITY_GATE:
|
|
return st21nfca_connectivity_event_received(hdev, host,
|
|
event, skb);
|
|
case ST21NFCA_APDU_READER_GATE:
|
|
return st21nfca_apdu_reader_event_received(hdev, event, skb);
|
|
default:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static struct nfc_hci_ops st21nfca_hci_ops = {
|
|
.open = st21nfca_hci_open,
|
|
.close = st21nfca_hci_close,
|
|
.load_session = st21nfca_hci_load_session,
|
|
.hci_ready = st21nfca_hci_ready,
|
|
.xmit = st21nfca_hci_xmit,
|
|
.start_poll = st21nfca_hci_start_poll,
|
|
.stop_poll = st21nfca_hci_stop_poll,
|
|
.dep_link_up = st21nfca_hci_dep_link_up,
|
|
.dep_link_down = st21nfca_hci_dep_link_down,
|
|
.target_from_gate = st21nfca_hci_target_from_gate,
|
|
.complete_target_discovered = st21nfca_hci_complete_target_discovered,
|
|
.im_transceive = st21nfca_hci_im_transceive,
|
|
.tm_send = st21nfca_hci_tm_send,
|
|
.check_presence = st21nfca_hci_check_presence,
|
|
.event_received = st21nfca_hci_event_received,
|
|
.cmd_received = st21nfca_hci_cmd_received,
|
|
.discover_se = st21nfca_hci_discover_se,
|
|
.enable_se = st21nfca_hci_enable_se,
|
|
.disable_se = st21nfca_hci_disable_se,
|
|
.se_io = st21nfca_hci_se_io,
|
|
};
|
|
|
|
int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops,
|
|
char *llc_name, int phy_headroom, int phy_tailroom,
|
|
int phy_payload, struct nfc_hci_dev **hdev,
|
|
struct st21nfca_se_status *se_status)
|
|
{
|
|
struct st21nfca_hci_info *info;
|
|
int r = 0;
|
|
int dev_num;
|
|
u32 protocols;
|
|
struct nfc_hci_init_data init_data;
|
|
unsigned long quirks = 0;
|
|
|
|
info = kzalloc(sizeof(struct st21nfca_hci_info), GFP_KERNEL);
|
|
if (!info) {
|
|
r = -ENOMEM;
|
|
goto err_alloc_hdev;
|
|
}
|
|
|
|
info->phy_ops = phy_ops;
|
|
info->phy_id = phy_id;
|
|
info->state = ST21NFCA_ST_COLD;
|
|
mutex_init(&info->info_lock);
|
|
|
|
init_data.gate_count = ARRAY_SIZE(st21nfca_gates);
|
|
|
|
memcpy(init_data.gates, st21nfca_gates, sizeof(st21nfca_gates));
|
|
|
|
/*
|
|
* Session id must include the driver name + i2c bus addr
|
|
* persistent info to discriminate 2 identical chips
|
|
*/
|
|
dev_num = find_first_zero_bit(dev_mask, ST21NFCA_NUM_DEVICES);
|
|
|
|
if (dev_num >= ST21NFCA_NUM_DEVICES)
|
|
return -ENODEV;
|
|
|
|
set_bit(dev_num, dev_mask);
|
|
|
|
scnprintf(init_data.session_id, sizeof(init_data.session_id), "%s%2x",
|
|
"ST21AH", dev_num);
|
|
|
|
protocols = NFC_PROTO_JEWEL_MASK |
|
|
NFC_PROTO_MIFARE_MASK |
|
|
NFC_PROTO_FELICA_MASK |
|
|
NFC_PROTO_ISO14443_MASK |
|
|
NFC_PROTO_ISO14443_B_MASK |
|
|
NFC_PROTO_ISO15693_MASK |
|
|
NFC_PROTO_NFC_DEP_MASK;
|
|
|
|
set_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &quirks);
|
|
|
|
info->hdev =
|
|
nfc_hci_allocate_device(&st21nfca_hci_ops, &init_data, quirks,
|
|
protocols, llc_name,
|
|
phy_headroom + ST21NFCA_CMDS_HEADROOM,
|
|
phy_tailroom, phy_payload);
|
|
|
|
if (!info->hdev) {
|
|
pr_err("Cannot allocate nfc hdev.\n");
|
|
r = -ENOMEM;
|
|
goto err_alloc_hdev;
|
|
}
|
|
|
|
info->se_status = se_status;
|
|
|
|
nfc_hci_set_clientdata(info->hdev, info);
|
|
|
|
r = nfc_hci_register_device(info->hdev);
|
|
if (r)
|
|
goto err_regdev;
|
|
|
|
*hdev = info->hdev;
|
|
st21nfca_dep_init(info->hdev);
|
|
st21nfca_se_init(info->hdev);
|
|
|
|
return 0;
|
|
|
|
err_regdev:
|
|
nfc_hci_free_device(info->hdev);
|
|
|
|
err_alloc_hdev:
|
|
kfree(info);
|
|
|
|
return r;
|
|
}
|
|
EXPORT_SYMBOL(st21nfca_hci_probe);
|
|
|
|
void st21nfca_hci_remove(struct nfc_hci_dev *hdev)
|
|
{
|
|
struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev);
|
|
|
|
st21nfca_dep_deinit(hdev);
|
|
st21nfca_se_deinit(hdev);
|
|
nfc_hci_unregister_device(hdev);
|
|
nfc_hci_free_device(hdev);
|
|
kfree(info);
|
|
}
|
|
EXPORT_SYMBOL(st21nfca_hci_remove);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION(DRIVER_DESC);
|