summaryrefslogtreecommitdiffstats
path: root/drivers/nfc/st-nci/se.c
diff options
context:
space:
mode:
authorChristophe Ricard <christophe.ricard@gmail.com>2015-10-25 22:54:39 +0100
committerSamuel Ortiz <sameo@linux.intel.com>2015-10-27 03:55:10 +0100
commit3648dc6d27f648b8e3ce9b48874627a833d53c3a (patch)
tree196aee67d701a3856d0ca3bfed0d43f1de20d0c6 /drivers/nfc/st-nci/se.c
parentbe73c2cbc857a4a3424c0e3cdd70002d5a27a756 (diff)
downloadlinux-3648dc6d27f648b8e3ce9b48874627a833d53c3a.tar.bz2
NFC: st-nci: Add ese-present/uicc-present dts properties
In order to align with st21nfca, dts configuration properties ese_present and uicc_present are made available in st-nci driver. So far, in early development firmware, because nci_nfcee_mode_set(DISABLE) was not supported we had to try to enable it during the secure element discovery phase. After several trials on commercial and qualified firmware it appears that nci_nfcee_mode_set(ENABLE) and nci_nfcee_mode_set(DISABLE) are properly supported. Such feature also help us to eventually save some time (~5ms) when only one secure element is connected. Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/nfc/st-nci/se.c')
-rw-r--r--drivers/nfc/st-nci/se.c98
1 files changed, 63 insertions, 35 deletions
diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
index 281288484794..147e2d904c63 100644
--- a/drivers/nfc/st-nci/se.c
+++ b/drivers/nfc/st-nci/se.c
@@ -419,12 +419,8 @@ void st_nci_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd,
}
EXPORT_SYMBOL_GPL(st_nci_hci_cmd_received);
-/*
- * Remarks: On some early st_nci firmware, nci_nfcee_mode_set(0)
- * is rejected
- */
static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
- u8 state)
+ u8 state)
{
struct st_nci_info *info = nci_get_drvdata(ndev);
int r;
@@ -449,7 +445,7 @@ static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
* retrieve a relevant host list.
*/
reinit_completion(&info->se_info.req_completion);
- r = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE);
+ r = nci_nfcee_mode_set(ndev, se_idx, state);
if (r != NCI_STATUS_OK)
return r;
@@ -465,7 +461,9 @@ static int st_nci_control_se(struct nci_dev *ndev, u8 se_idx,
* There is no possible synchronization to prevent this.
* Adding a small delay is the only way to solve the issue.
*/
- usleep_range(3000, 5000);
+ if (info->se_info.se_status->is_ese_present &&
+ info->se_info.se_status->is_uicc_present)
+ usleep_range(3000, 5000);
r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE,
NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list);
@@ -488,11 +486,20 @@ int st_nci_disable_se(struct nci_dev *ndev, u32 se_idx)
pr_debug("st_nci_disable_se\n");
- if (se_idx == NFC_SE_EMBEDDED) {
- r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
- ST_NCI_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0);
- if (r < 0)
- return r;
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_info.se_status->is_uicc_enable is true should never happen
+ * Same for eSE.
+ */
+ r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_OFF);
+ if (r < 0) {
+ /* Do best effort to release SWP */
+ if (se_idx == NFC_SE_EMBEDDED) {
+ r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
+ ST_NCI_EVT_SE_END_OF_APDU_TRANSFER,
+ NULL, 0);
+ }
+ return r;
}
return 0;
@@ -505,11 +512,25 @@ int st_nci_enable_se(struct nci_dev *ndev, u32 se_idx)
pr_debug("st_nci_enable_se\n");
- if (se_idx == ST_NCI_HCI_HOST_ID_ESE) {
+ /*
+ * According to upper layer, se_idx == NFC_SE_UICC when
+ * info->se_info.se_status->is_uicc_enable is true should never happen.
+ * Same for eSE.
+ */
+ r = st_nci_control_se(ndev, se_idx, ST_NCI_SE_MODE_ON);
+ if (r == ST_NCI_HCI_HOST_ID_ESE) {
+ st_nci_se_get_atr(ndev);
r = nci_hci_send_event(ndev, ST_NCI_APDU_READER_GATE,
ST_NCI_EVT_SE_SOFT_RESET, NULL, 0);
- if (r < 0)
- return r;
+ }
+
+ if (r < 0) {
+ /*
+ * The activation procedure failed, the secure element
+ * is not connected. Remove from the list.
+ */
+ nfc_remove_se(ndev->nfc_dev, se_idx);
+ return r;
}
return 0;
@@ -592,8 +613,8 @@ exit:
int st_nci_discover_se(struct nci_dev *ndev)
{
- u8 param[2];
- int r;
+ u8 white_list[2];
+ int r, wl_size = 0;
int se_count = 0;
struct st_nci_info *info = nci_get_drvdata(ndev);
@@ -606,29 +627,34 @@ int st_nci_discover_se(struct nci_dev *ndev)
if (test_bit(ST_NCI_FACTORY_MODE, &info->flags))
return 0;
- param[0] = ST_NCI_UICC_HOST_ID;
- param[1] = ST_NCI_HCI_HOST_ID_ESE;
- r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
- NCI_HCI_ADMIN_PARAM_WHITELIST,
- param, sizeof(param));
- if (r != NCI_HCI_ANY_OK)
- return r;
+ if (info->se_info.se_status->is_ese_present &&
+ info->se_info.se_status->is_uicc_present) {
+ white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
+ white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
+ } else if (!info->se_info.se_status->is_ese_present &&
+ info->se_info.se_status->is_uicc_present) {
+ white_list[wl_size++] = ST_NCI_UICC_HOST_ID;
+ } else if (info->se_info.se_status->is_ese_present &&
+ !info->se_info.se_status->is_uicc_present) {
+ white_list[wl_size++] = ST_NCI_ESE_HOST_ID;
+ }
+
+ if (wl_size) {
+ r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE,
+ NCI_HCI_ADMIN_PARAM_WHITELIST,
+ white_list, wl_size);
+ if (r != NCI_HCI_ANY_OK)
+ return r;
+ }
- r = st_nci_control_se(ndev, ST_NCI_UICC_HOST_ID,
- ST_NCI_SE_MODE_ON);
- if (r == ST_NCI_UICC_HOST_ID) {
+ if (info->se_info.se_status->is_uicc_present) {
nfc_add_se(ndev->nfc_dev, ST_NCI_UICC_HOST_ID, NFC_SE_UICC);
se_count++;
}
- /* Try to enable eSE in order to check availability */
- r = st_nci_control_se(ndev, ST_NCI_HCI_HOST_ID_ESE,
- ST_NCI_SE_MODE_ON);
- if (r == ST_NCI_HCI_HOST_ID_ESE) {
- nfc_add_se(ndev->nfc_dev, ST_NCI_HCI_HOST_ID_ESE,
- NFC_SE_EMBEDDED);
+ if (info->se_info.se_status->is_ese_present) {
+ nfc_add_se(ndev->nfc_dev, ST_NCI_ESE_HOST_ID, NFC_SE_EMBEDDED);
se_count++;
- st_nci_se_get_atr(ndev);
}
return !se_count;
@@ -701,7 +727,7 @@ static void st_nci_se_activation_timeout(unsigned long data)
complete(&info->se_info.req_completion);
}
-int st_nci_se_init(struct nci_dev *ndev)
+int st_nci_se_init(struct nci_dev *ndev, struct st_nci_se_status *se_status)
{
struct st_nci_info *info = nci_get_drvdata(ndev);
@@ -723,6 +749,8 @@ int st_nci_se_init(struct nci_dev *ndev)
info->se_info.wt_timeout =
ST_NCI_BWI_TO_TIMEOUT(ST_NCI_ATR_DEFAULT_BWI);
+ info->se_info.se_status = se_status;
+
return 0;
}
EXPORT_SYMBOL(st_nci_se_init);