2015-02-01 22:26:15 +01:00
/*
2015-06-09 22:26:05 +02:00
* Secure Element driver for STMicroelectronics NFC NCI chip
2015-02-01 22:26:15 +01:00
*
2015-06-09 22:26:05 +02:00
* Copyright ( C ) 2014 - 2015 STMicroelectronics SAS . All rights reserved .
2015-02-01 22:26:15 +01:00
*
* 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 <linux/delay.h>
# include <net/nfc/nci.h>
# include <net/nfc/nci_core.h>
2015-06-09 22:26:05 +02:00
# include "st-nci.h"
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
struct st_nci_pipe_info {
2015-02-01 22:26:15 +01:00
u8 pipe_state ;
u8 src_host_id ;
u8 src_gate_id ;
u8 dst_host_id ;
u8 dst_gate_id ;
} __packed ;
/* Hosts */
2015-06-09 22:26:05 +02:00
# define ST_NCI_HOST_CONTROLLER_ID 0x00
# define ST_NCI_TERMINAL_HOST_ID 0x01
# define ST_NCI_UICC_HOST_ID 0x02
# define ST_NCI_ESE_HOST_ID 0xc0
2015-02-01 22:26:15 +01:00
/* Gates */
2015-06-09 22:26:05 +02:00
# define ST_NCI_APDU_READER_GATE 0xf0
# define ST_NCI_CONNECTIVITY_GATE 0x41
2015-02-01 22:26:15 +01:00
/* Pipes */
2015-06-09 22:26:05 +02:00
# define ST_NCI_DEVICE_MGNT_PIPE 0x02
2015-02-01 22:26:15 +01:00
/* Connectivity pipe only */
2015-06-09 22:26:05 +02:00
# define ST_NCI_SE_COUNT_PIPE_UICC 0x01
2015-02-01 22:26:15 +01:00
/* Connectivity + APDU Reader pipe */
2015-06-09 22:26:05 +02:00
# define ST_NCI_SE_COUNT_PIPE_EMBEDDED 0x02
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_SE_TO_HOT_PLUG 1000 /* msecs */
# define ST_NCI_SE_TO_PIPES 2000
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80)
2015-02-01 22:26:15 +01:00
# define NCI_HCI_APDU_PARAM_ATR 0x01
# define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01
# define NCI_HCI_ADMIN_PARAM_WHITELIST 0x03
# define NCI_HCI_ADMIN_PARAM_HOST_LIST 0x04
2015-06-09 22:26:05 +02:00
# define ST_NCI_EVT_SE_HARD_RESET 0x20
# define ST_NCI_EVT_TRANSMIT_DATA 0x10
2015-10-25 22:54:44 +01:00
# define ST_NCI_EVT_WTX_REQUEST 0x11
2015-06-09 22:26:05 +02:00
# define ST_NCI_EVT_SE_SOFT_RESET 0x11
# define ST_NCI_EVT_SE_END_OF_APDU_TRANSFER 0x21
# define ST_NCI_EVT_HOT_PLUG 0x03
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_SE_MODE_OFF 0x00
# define ST_NCI_SE_MODE_ON 0x01
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_EVT_CONNECTIVITY 0x10
# define ST_NCI_EVT_TRANSACTION 0x12
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_DM_GETINFO 0x13
# define ST_NCI_DM_GETINFO_PIPE_LIST 0x02
# define ST_NCI_DM_GETINFO_PIPE_INFO 0x01
# define ST_NCI_DM_PIPE_CREATED 0x02
# define ST_NCI_DM_PIPE_OPEN 0x04
# define ST_NCI_DM_RF_ACTIVE 0x80
# define ST_NCI_DM_DISCONNECT 0x30
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_DM_IS_PIPE_OPEN(p) \
( ( p & 0x0f ) = = ( ST_NCI_DM_PIPE_CREATED | ST_NCI_DM_PIPE_OPEN ) )
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_ATR_DEFAULT_BWI 0x04
2015-02-01 22:26:15 +01:00
/*
* WT = 2 ^ BWI / 10 [ s ] , convert into msecs and add a secure
* room by increasing by 2 this timeout
*/
2015-06-09 22:26:05 +02:00
# define ST_NCI_BWI_TO_TIMEOUT(x) ((1 << x) * 200)
# define ST_NCI_ATR_GET_Y_FROM_TD(x) (x >> 4)
2015-02-01 22:26:15 +01:00
/* If TA is present bit 0 is set */
2015-06-09 22:26:05 +02:00
# define ST_NCI_ATR_TA_PRESENT(x) (x & 0x01)
2015-02-01 22:26:15 +01:00
/* If TB is present bit 1 is set */
2015-06-09 22:26:05 +02:00
# define ST_NCI_ATR_TB_PRESENT(x) (x & 0x02)
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
# define ST_NCI_NUM_DEVICES 256
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
static DECLARE_BITMAP ( dev_mask , ST_NCI_NUM_DEVICES ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
/* Here are the mandatory pipe for st_nci */
static struct nci_hci_gate st_nci_gates [ ] = {
2015-02-01 22:26:15 +01:00
{ NCI_HCI_ADMIN_GATE , NCI_HCI_ADMIN_PIPE ,
2015-06-09 22:26:05 +02:00
ST_NCI_HOST_CONTROLLER_ID } ,
2015-02-01 22:26:15 +01:00
{ NCI_HCI_LINK_MGMT_GATE , NCI_HCI_LINK_MGMT_PIPE ,
2015-06-09 22:26:05 +02:00
ST_NCI_HOST_CONTROLLER_ID } ,
{ ST_NCI_DEVICE_MGNT_GATE , ST_NCI_DEVICE_MGNT_PIPE ,
ST_NCI_HOST_CONTROLLER_ID } ,
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:33 +01:00
{ NCI_HCI_IDENTITY_MGMT_GATE , NCI_HCI_INVALID_PIPE ,
ST_NCI_HOST_CONTROLLER_ID } ,
2015-10-25 22:54:36 +01:00
{ NCI_HCI_LOOPBACK_GATE , NCI_HCI_INVALID_PIPE ,
ST_NCI_HOST_CONTROLLER_ID } ,
2015-10-25 22:54:33 +01:00
2015-02-01 22:26:15 +01:00
/* Secure element pipes are created by secure element host */
2015-06-09 22:26:05 +02:00
{ ST_NCI_CONNECTIVITY_GATE , NCI_HCI_DO_NOT_OPEN_PIPE ,
ST_NCI_HOST_CONTROLLER_ID } ,
{ ST_NCI_APDU_READER_GATE , NCI_HCI_DO_NOT_OPEN_PIPE ,
ST_NCI_HOST_CONTROLLER_ID } ,
2015-02-01 22:26:15 +01:00
} ;
2015-06-09 22:26:05 +02:00
static u8 st_nci_se_get_bwi ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
int i ;
u8 td ;
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
/* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */
2015-06-09 22:26:05 +02:00
for ( i = 1 ; i < ST_NCI_ESE_MAX_LENGTH ; i + + ) {
td = ST_NCI_ATR_GET_Y_FROM_TD ( info - > se_info . atr [ i ] ) ;
if ( ST_NCI_ATR_TA_PRESENT ( td ) )
2015-02-01 22:26:15 +01:00
i + + ;
2015-06-09 22:26:05 +02:00
if ( ST_NCI_ATR_TB_PRESENT ( td ) ) {
2015-02-01 22:26:15 +01:00
i + + ;
return info - > se_info . atr [ i ] > > 4 ;
}
}
2015-06-09 22:26:05 +02:00
return ST_NCI_ATR_DEFAULT_BWI ;
2015-02-01 22:26:15 +01:00
}
2015-06-09 22:26:05 +02:00
static void st_nci_se_get_atr ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
int r ;
struct sk_buff * skb ;
2015-06-09 22:26:05 +02:00
r = nci_hci_get_param ( ndev , ST_NCI_APDU_READER_GATE ,
2015-02-01 22:26:15 +01:00
NCI_HCI_APDU_PARAM_ATR , & skb ) ;
if ( r < 0 )
return ;
2015-06-09 22:26:05 +02:00
if ( skb - > len < = ST_NCI_ESE_MAX_LENGTH ) {
2015-02-01 22:26:15 +01:00
memcpy ( info - > se_info . atr , skb - > data , skb - > len ) ;
info - > se_info . wt_timeout =
2015-06-09 22:26:05 +02:00
ST_NCI_BWI_TO_TIMEOUT ( st_nci_se_get_bwi ( ndev ) ) ;
2015-02-01 22:26:15 +01:00
}
kfree_skb ( skb ) ;
}
2015-06-09 22:26:05 +02:00
int st_nci_hci_load_session ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
int i , j , r ;
struct sk_buff * skb_pipe_list , * skb_pipe_info ;
2015-06-09 22:26:05 +02:00
struct st_nci_pipe_info * dm_pipe_info ;
u8 pipe_list [ ] = { ST_NCI_DM_GETINFO_PIPE_LIST ,
ST_NCI_TERMINAL_HOST_ID } ;
u8 pipe_info [ ] = { ST_NCI_DM_GETINFO_PIPE_INFO ,
ST_NCI_TERMINAL_HOST_ID , 0 } ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
/* On ST_NCI device pipes number are dynamics
2015-02-01 22:26:15 +01:00
* 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 )
2015-06-09 22:26:05 +02:00
* A better approach on ST_NCI is to :
2015-02-01 22:26:15 +01:00
* - get a pipe list for each host .
2015-06-09 22:26:05 +02:00
* ( eg : ST_NCI_HOST_CONTROLLER_ID for now ) .
2015-02-01 22:26:15 +01:00
* ( TODO Later on UICC HOST and eSE HOST )
* - get pipe information
2015-06-09 22:26:05 +02:00
* - match retrieved pipe list in st_nci_gates
* ST_NCI_DEVICE_MGNT_GATE is a proprietary gate
* with ST_NCI_DEVICE_MGNT_PIPE .
2015-02-01 22:26:15 +01:00
* Pipe can be closed and need to be open .
*/
2015-06-09 22:26:05 +02:00
r = nci_hci_connect_gate ( ndev , ST_NCI_HOST_CONTROLLER_ID ,
ST_NCI_DEVICE_MGNT_GATE ,
ST_NCI_DEVICE_MGNT_PIPE ) ;
2015-02-01 22:26:15 +01:00
if ( r < 0 )
2015-08-14 22:33:34 +02:00
return r ;
2015-02-01 22:26:15 +01:00
/* Get pipe list */
2015-06-09 22:26:05 +02:00
r = nci_hci_send_cmd ( ndev , ST_NCI_DEVICE_MGNT_GATE ,
ST_NCI_DM_GETINFO , pipe_list , sizeof ( pipe_list ) ,
2015-02-01 22:26:15 +01:00
& skb_pipe_list ) ;
if ( r < 0 )
2015-08-14 22:33:34 +02:00
return r ;
2015-02-01 22:26:15 +01:00
/* Complete the existing gate_pipe table */
for ( i = 0 ; i < skb_pipe_list - > len ; i + + ) {
pipe_info [ 2 ] = skb_pipe_list - > data [ i ] ;
2015-06-09 22:26:05 +02:00
r = nci_hci_send_cmd ( ndev , ST_NCI_DEVICE_MGNT_GATE ,
ST_NCI_DM_GETINFO , pipe_info ,
2015-02-01 22:26:15 +01:00
sizeof ( pipe_info ) , & skb_pipe_info ) ;
if ( r )
continue ;
/*
* Match pipe ID and gate ID
* Output format from ST21NFC_DM_GETINFO is :
* - pipe state ( 1 byte )
* - source hid ( 1 byte )
* - source gid ( 1 byte )
* - destination hid ( 1 byte )
* - destination gid ( 1 byte )
*/
2015-06-09 22:26:05 +02:00
dm_pipe_info = ( struct st_nci_pipe_info * ) skb_pipe_info - > data ;
if ( dm_pipe_info - > dst_gate_id = = ST_NCI_APDU_READER_GATE & &
dm_pipe_info - > src_host_id ! = ST_NCI_ESE_HOST_ID ) {
2015-02-01 22:26:15 +01:00
pr_err ( " Unexpected apdu_reader pipe on host %x \n " ,
dm_pipe_info - > src_host_id ) ;
2015-08-14 22:33:34 +02:00
kfree_skb ( skb_pipe_info ) ;
2015-02-01 22:26:15 +01:00
continue ;
}
2015-10-25 22:54:34 +01:00
for ( j = 3 ; ( j < ARRAY_SIZE ( st_nci_gates ) ) & &
2015-06-09 22:26:05 +02:00
( st_nci_gates [ j ] . gate ! = dm_pipe_info - > dst_gate_id ) ; j + + )
2015-02-01 22:26:15 +01:00
;
2015-06-09 22:26:05 +02:00
if ( j < ARRAY_SIZE ( st_nci_gates ) & &
st_nci_gates [ j ] . gate = = dm_pipe_info - > dst_gate_id & &
ST_NCI_DM_IS_PIPE_OPEN ( dm_pipe_info - > pipe_state ) ) {
2015-10-25 22:54:30 +01:00
ndev - > hci_dev - > init_data . gates [ j ] . pipe = pipe_info [ 2 ] ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
ndev - > hci_dev - > gate2pipe [ st_nci_gates [ j ] . gate ] =
2015-10-25 22:54:30 +01:00
pipe_info [ 2 ] ;
ndev - > hci_dev - > pipes [ pipe_info [ 2 ] ] . gate =
2015-06-09 22:26:05 +02:00
st_nci_gates [ j ] . gate ;
2015-10-25 22:54:30 +01:00
ndev - > hci_dev - > pipes [ pipe_info [ 2 ] ] . host =
2015-02-01 22:26:15 +01:00
dm_pipe_info - > src_host_id ;
}
2015-08-14 22:33:34 +02:00
kfree_skb ( skb_pipe_info ) ;
2015-02-01 22:26:15 +01:00
}
2015-10-25 22:54:28 +01:00
/*
* 3 gates have a well known pipe ID . Only NCI_HCI_LINK_MGMT_GATE
* is not yet open at this stage .
*/
r = nci_hci_connect_gate ( ndev , ST_NCI_HOST_CONTROLLER_ID ,
NCI_HCI_LINK_MGMT_GATE ,
NCI_HCI_LINK_MGMT_PIPE ) ;
2015-02-01 22:26:15 +01:00
kfree_skb ( skb_pipe_list ) ;
return r ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_hci_load_session ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
static void st_nci_hci_admin_event_received ( struct nci_dev * ndev ,
2015-02-01 22:26:15 +01:00
u8 event , struct sk_buff * skb )
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
switch ( event ) {
2015-06-09 22:26:05 +02:00
case ST_NCI_EVT_HOT_PLUG :
2015-02-01 22:26:15 +01:00
if ( info - > se_info . se_active ) {
2015-06-09 22:26:05 +02:00
if ( ! ST_NCI_EVT_HOT_PLUG_IS_INHIBITED ( skb ) ) {
2015-02-01 22:26:15 +01:00
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 +
2015-06-09 22:26:05 +02:00
msecs_to_jiffies ( ST_NCI_SE_TO_PIPES ) ) ;
2015-02-01 22:26:15 +01:00
}
}
break ;
2015-10-25 22:54:37 +01:00
default :
nfc_err ( & ndev - > nfc_dev - > dev , " Unexpected event on admin gate \n " ) ;
2015-02-01 22:26:15 +01:00
}
}
2015-06-09 22:26:05 +02:00
static int st_nci_hci_apdu_reader_event_received ( struct nci_dev * ndev ,
2015-02-01 22:26:15 +01:00
u8 event ,
struct sk_buff * skb )
{
int r = 0 ;
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
pr_debug ( " apdu reader gate event: %x \n " , event ) ;
switch ( event ) {
2015-06-09 22:26:05 +02:00
case ST_NCI_EVT_TRANSMIT_DATA :
2015-02-01 22:26:15 +01:00
del_timer_sync ( & info - > se_info . bwi_timer ) ;
info - > se_info . bwi_active = false ;
info - > se_info . cb ( info - > se_info . cb_context ,
skb - > data , skb - > len , 0 ) ;
break ;
2015-06-09 22:26:05 +02:00
case ST_NCI_EVT_WTX_REQUEST :
2015-02-01 22:26:15 +01:00
mod_timer ( & info - > se_info . bwi_timer , jiffies +
msecs_to_jiffies ( info - > se_info . wt_timeout ) ) ;
break ;
2015-10-25 22:54:37 +01:00
default :
nfc_err ( & ndev - > nfc_dev - > dev , " Unexpected event on apdu reader gate \n " ) ;
return 1 ;
2015-02-01 22:26:15 +01:00
}
kfree_skb ( skb ) ;
return r ;
}
/*
* Returns :
* < = 0 : driver handled the event , skb consumed
* 1 : driver does not handle the event , please do standard processing
*/
2015-06-09 22:26:05 +02:00
static int st_nci_hci_connectivity_event_received ( struct nci_dev * ndev ,
2015-02-01 22:26:15 +01:00
u8 host , u8 event ,
struct sk_buff * skb )
{
int r = 0 ;
2015-02-01 22:26:19 +01:00
struct device * dev = & ndev - > nfc_dev - > dev ;
struct nfc_evt_transaction * transaction ;
2015-02-01 22:26:15 +01:00
pr_debug ( " connectivity gate event: %x \n " , event ) ;
switch ( event ) {
2015-06-09 22:26:05 +02:00
case ST_NCI_EVT_CONNECTIVITY :
2015-02-01 22:26:19 +01:00
2015-02-01 22:26:15 +01:00
break ;
2015-06-09 22:26:05 +02:00
case ST_NCI_EVT_TRANSACTION :
2015-03-31 08:02:22 +02:00
/* According to specification etsi 102 622
* 11.2 .2 .4 EVT_TRANSACTION Table 52
* Description Tag Length
* AID 81 5 to 16
* PARAMETERS 82 0 to 255
*/
2015-02-01 22:26:19 +01:00
if ( skb - > len < NFC_MIN_AID_LENGTH + 2 & &
skb - > data [ 0 ] ! = NFC_EVT_TRANSACTION_AID_TAG )
return - EPROTO ;
transaction = ( struct nfc_evt_transaction * ) devm_kzalloc ( dev ,
skb - > len - 2 , GFP_KERNEL ) ;
transaction - > aid_len = skb - > data [ 1 ] ;
2015-03-31 08:02:22 +02:00
memcpy ( transaction - > aid , & skb - > data [ 2 ] , transaction - > aid_len ) ;
2015-02-01 22:26:19 +01:00
2015-03-31 08:02:22 +02:00
/* Check next byte is PARAMETERS tag (82) */
2015-02-01 22:26:19 +01:00
if ( skb - > data [ transaction - > aid_len + 2 ] ! =
NFC_EVT_TRANSACTION_PARAMS_TAG )
return - EPROTO ;
transaction - > params_len = skb - > data [ transaction - > aid_len + 3 ] ;
memcpy ( transaction - > params , skb - > data +
transaction - > aid_len + 4 , transaction - > params_len ) ;
r = nfc_se_transaction ( ndev - > nfc_dev , host , transaction ) ;
2015-03-31 08:02:20 +02:00
break ;
2015-02-01 22:26:15 +01:00
default :
2015-10-25 22:54:37 +01:00
nfc_err ( & ndev - > nfc_dev - > dev , " Unexpected event on connectivity gate \n " ) ;
2015-02-01 22:26:15 +01:00
return 1 ;
}
kfree_skb ( skb ) ;
return r ;
}
2015-06-09 22:26:05 +02:00
void st_nci_hci_event_received ( struct nci_dev * ndev , u8 pipe ,
2015-02-01 22:26:15 +01:00
u8 event , struct sk_buff * skb )
{
u8 gate = ndev - > hci_dev - > pipes [ pipe ] . gate ;
u8 host = ndev - > hci_dev - > pipes [ pipe ] . host ;
switch ( gate ) {
case NCI_HCI_ADMIN_GATE :
2015-06-09 22:26:05 +02:00
st_nci_hci_admin_event_received ( ndev , event , skb ) ;
2015-02-01 22:26:15 +01:00
break ;
2015-06-09 22:26:05 +02:00
case ST_NCI_APDU_READER_GATE :
st_nci_hci_apdu_reader_event_received ( ndev , event , skb ) ;
2015-02-01 22:26:15 +01:00
break ;
2015-06-09 22:26:05 +02:00
case ST_NCI_CONNECTIVITY_GATE :
2015-10-25 22:54:36 +01:00
st_nci_hci_connectivity_event_received ( ndev , host , event , skb ) ;
break ;
case NCI_HCI_LOOPBACK_GATE :
st_nci_hci_loopback_event_received ( ndev , event , skb ) ;
2015-02-01 22:26:15 +01:00
break ;
}
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_hci_event_received ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
void st_nci_hci_cmd_received ( struct nci_dev * ndev , u8 pipe , u8 cmd ,
2015-02-01 22:26:15 +01:00
struct sk_buff * skb )
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
u8 gate = ndev - > hci_dev - > pipes [ pipe ] . gate ;
pr_debug ( " cmd: %x \n " , cmd ) ;
switch ( cmd ) {
case NCI_HCI_ANY_OPEN_PIPE :
2015-06-09 22:26:05 +02:00
if ( gate ! = ST_NCI_APDU_READER_GATE & &
ndev - > hci_dev - > pipes [ pipe ] . host ! = ST_NCI_UICC_HOST_ID )
2015-02-01 22:26:15 +01:00
ndev - > hci_dev - > count_pipes + + ;
if ( ndev - > hci_dev - > count_pipes = =
ndev - > hci_dev - > expected_pipes ) {
del_timer_sync ( & info - > se_info . se_active_timer ) ;
info - > se_info . se_active = false ;
ndev - > hci_dev - > count_pipes = 0 ;
complete ( & info - > se_info . req_completion ) ;
}
break ;
}
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_hci_cmd_received ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
static int st_nci_control_se ( struct nci_dev * ndev , u8 se_idx ,
2015-10-25 22:54:39 +01:00
u8 state )
2015-02-01 22:26:15 +01:00
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-10-25 22:54:41 +01:00
int r , i ;
2015-02-01 22:26:15 +01:00
struct sk_buff * sk_host_list ;
u8 host_id ;
switch ( se_idx ) {
2015-06-09 22:26:05 +02:00
case ST_NCI_UICC_HOST_ID :
2015-02-01 22:26:15 +01:00
ndev - > hci_dev - > count_pipes = 0 ;
2015-06-09 22:26:05 +02:00
ndev - > hci_dev - > expected_pipes = ST_NCI_SE_COUNT_PIPE_UICC ;
2015-02-01 22:26:15 +01:00
break ;
2015-06-09 22:26:05 +02:00
case ST_NCI_ESE_HOST_ID :
2015-02-01 22:26:15 +01:00
ndev - > hci_dev - > count_pipes = 0 ;
2015-06-09 22:26:05 +02:00
ndev - > hci_dev - > expected_pipes = ST_NCI_SE_COUNT_PIPE_EMBEDDED ;
2015-02-01 22:26:15 +01:00
break ;
default :
return - EINVAL ;
}
/*
* Wait for an EVT_HOT_PLUG in order to
* retrieve a relevant host list .
*/
reinit_completion ( & info - > se_info . req_completion ) ;
2015-10-25 22:54:39 +01:00
r = nci_nfcee_mode_set ( ndev , se_idx , state ) ;
2015-02-01 22:26:15 +01:00
if ( r ! = NCI_STATUS_OK )
return r ;
mod_timer ( & info - > se_info . se_active_timer , jiffies +
2015-06-09 22:26:05 +02:00
msecs_to_jiffies ( ST_NCI_SE_TO_HOT_PLUG ) ) ;
2015-02-01 22:26:15 +01:00
info - > se_info . se_active = true ;
/* Ignore return value and check in any case the host_list */
wait_for_completion_interruptible ( & info - > se_info . req_completion ) ;
/* There might be some "collision" after receiving a HOT_PLUG event
* This may cause the CLF to not answer to the next hci command .
* There is no possible synchronization to prevent this .
* Adding a small delay is the only way to solve the issue .
*/
2015-10-25 22:54:39 +01:00
if ( info - > se_info . se_status - > is_ese_present & &
info - > se_info . se_status - > is_uicc_present )
2015-10-25 22:54:40 +01:00
usleep_range ( 15000 , 20000 ) ;
2015-02-01 22:26:15 +01:00
r = nci_hci_get_param ( ndev , NCI_HCI_ADMIN_GATE ,
NCI_HCI_ADMIN_PARAM_HOST_LIST , & sk_host_list ) ;
if ( r ! = NCI_HCI_ANY_OK )
return r ;
2015-10-25 22:54:41 +01:00
for ( i = 0 ; i < sk_host_list - > len & &
sk_host_list - > data [ i ] ! = se_idx ; i + + )
;
host_id = sk_host_list - > data [ i ] ;
2015-02-01 22:26:15 +01:00
kfree_skb ( sk_host_list ) ;
2015-06-09 22:26:05 +02:00
if ( state = = ST_NCI_SE_MODE_ON & & host_id = = se_idx )
2015-02-01 22:26:15 +01:00
return se_idx ;
2015-06-09 22:26:05 +02:00
else if ( state = = ST_NCI_SE_MODE_OFF & & host_id ! = se_idx )
2015-02-01 22:26:15 +01:00
return se_idx ;
return - 1 ;
}
2015-06-09 22:26:05 +02:00
int st_nci_disable_se ( struct nci_dev * ndev , u32 se_idx )
2015-02-01 22:26:15 +01:00
{
int r ;
2015-06-09 22:26:05 +02:00
pr_debug ( " st_nci_disable_se \n " ) ;
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:39 +01:00
/*
* 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 ;
2015-02-01 22:26:15 +01:00
}
return 0 ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_disable_se ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
int st_nci_enable_se ( struct nci_dev * ndev , u32 se_idx )
2015-02-01 22:26:15 +01:00
{
int r ;
2015-06-09 22:26:05 +02:00
pr_debug ( " st_nci_enable_se \n " ) ;
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:39 +01:00
/*
* 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 ) ;
2015-06-09 22:26:05 +02:00
r = nci_hci_send_event ( ndev , ST_NCI_APDU_READER_GATE ,
ST_NCI_EVT_SE_SOFT_RESET , NULL , 0 ) ;
2015-10-25 22:54:39 +01:00
}
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 ;
2015-02-01 22:26:15 +01:00
}
return 0 ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_enable_se ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
static int st_nci_hci_network_init ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
2015-10-25 22:54:36 +01:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-03 19:48:05 +01:00
struct core_conn_create_dest_spec_params * dest_params ;
struct dest_spec_params spec_params ;
2015-02-01 22:26:15 +01:00
struct nci_conn_info * conn_info ;
int r , dev_num ;
2015-02-03 19:48:05 +01:00
dest_params =
kzalloc ( sizeof ( struct core_conn_create_dest_spec_params ) +
sizeof ( struct dest_spec_params ) , GFP_KERNEL ) ;
if ( dest_params = = NULL ) {
r = - ENOMEM ;
2015-02-01 22:26:15 +01:00
goto exit ;
2015-02-03 19:48:05 +01:00
}
dest_params - > type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE ;
dest_params - > length = sizeof ( struct dest_spec_params ) ;
2015-02-03 19:48:07 +01:00
spec_params . id = ndev - > hci_dev - > nfcee_id ;
2015-02-03 19:48:05 +01:00
spec_params . protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS ;
2015-06-09 22:26:05 +02:00
memcpy ( dest_params - > value , & spec_params ,
sizeof ( struct dest_spec_params ) ) ;
2015-02-03 19:48:05 +01:00
r = nci_core_conn_create ( ndev , NCI_DESTINATION_NFCEE , 1 ,
sizeof ( struct core_conn_create_dest_spec_params ) +
sizeof ( struct dest_spec_params ) ,
dest_params ) ;
if ( r ! = NCI_STATUS_OK )
goto free_dest_params ;
2015-02-01 22:26:15 +01:00
conn_info = ndev - > hci_dev - > conn_info ;
if ( ! conn_info )
2015-02-03 19:48:05 +01:00
goto free_dest_params ;
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:32 +01:00
ndev - > hci_dev - > init_data . gate_count = ARRAY_SIZE ( st_nci_gates ) ;
2015-06-09 22:26:05 +02:00
memcpy ( ndev - > hci_dev - > init_data . gates , st_nci_gates ,
sizeof ( st_nci_gates ) ) ;
2015-02-01 22:26:15 +01:00
/*
* Session id must include the driver name + i2c bus addr
* persistent info to discriminate 2 identical chips
*/
2015-06-09 22:26:05 +02:00
dev_num = find_first_zero_bit ( dev_mask , ST_NCI_NUM_DEVICES ) ;
if ( dev_num > = ST_NCI_NUM_DEVICES ) {
2015-02-03 19:48:05 +01:00
r = - ENODEV ;
goto free_dest_params ;
}
2015-02-01 22:26:15 +01:00
scnprintf ( ndev - > hci_dev - > init_data . session_id ,
sizeof ( ndev - > hci_dev - > init_data . session_id ) ,
" %s%2x " , " ST21BH " , dev_num ) ;
r = nci_hci_dev_session_init ( ndev ) ;
if ( r ! = NCI_HCI_ANY_OK )
2015-03-31 08:02:14 +02:00
goto free_dest_params ;
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:36 +01:00
/*
* In factory mode , we prevent secure elements activation
* by disabling nfcee on the current HCI connection id .
* HCI will be used here only for proprietary commands .
*/
if ( test_bit ( ST_NCI_FACTORY_MODE , & info - > flags ) )
r = nci_nfcee_mode_set ( ndev , ndev - > hci_dev - > conn_info - > id ,
NCI_NFCEE_DISABLE ) ;
else
r = nci_nfcee_mode_set ( ndev , ndev - > hci_dev - > conn_info - > id ,
NCI_NFCEE_ENABLE ) ;
2015-02-01 22:26:15 +01:00
2015-02-03 19:48:05 +01:00
free_dest_params :
kfree ( dest_params ) ;
2015-02-01 22:26:15 +01:00
exit :
return r ;
}
2015-06-09 22:26:05 +02:00
int st_nci_discover_se ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
2015-10-25 22:54:39 +01:00
u8 white_list [ 2 ] ;
int r , wl_size = 0 ;
2015-02-01 22:26:15 +01:00
int se_count = 0 ;
2015-10-25 22:54:36 +01:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
pr_debug ( " st_nci_discover_se \n " ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
r = st_nci_hci_network_init ( ndev ) ;
2015-02-01 22:26:15 +01:00
if ( r ! = 0 )
return r ;
2015-10-25 22:54:36 +01:00
if ( test_bit ( ST_NCI_FACTORY_MODE , & info - > flags ) )
return 0 ;
2015-10-25 22:54:39 +01:00
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 ;
}
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:39 +01:00
if ( info - > se_info . se_status - > is_uicc_present ) {
2015-06-09 22:26:05 +02:00
nfc_add_se ( ndev - > nfc_dev , ST_NCI_UICC_HOST_ID , NFC_SE_UICC ) ;
2015-02-01 22:26:15 +01:00
se_count + + ;
}
2015-10-25 22:54:39 +01:00
if ( info - > se_info . se_status - > is_ese_present ) {
nfc_add_se ( ndev - > nfc_dev , ST_NCI_ESE_HOST_ID , NFC_SE_EMBEDDED ) ;
2015-02-01 22:26:15 +01:00
se_count + + ;
}
return ! se_count ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL_GPL ( st_nci_discover_se ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
int st_nci_se_io ( struct nci_dev * ndev , u32 se_idx ,
2015-02-01 22:26:15 +01:00
u8 * apdu , size_t apdu_length ,
se_io_cb_t cb , void * cb_context )
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
pr_debug ( " \n " ) ;
switch ( se_idx ) {
2015-06-09 22:26:05 +02:00
case ST_NCI_HCI_HOST_ID_ESE :
2015-02-01 22:26:15 +01:00
info - > se_info . cb = cb ;
info - > se_info . cb_context = cb_context ;
mod_timer ( & info - > se_info . bwi_timer , jiffies +
msecs_to_jiffies ( info - > se_info . wt_timeout ) ) ;
info - > se_info . bwi_active = true ;
2015-06-09 22:26:05 +02:00
return nci_hci_send_event ( ndev , ST_NCI_APDU_READER_GATE ,
ST_NCI_EVT_TRANSMIT_DATA , apdu ,
2015-02-01 22:26:15 +01:00
apdu_length ) ;
default :
return - ENODEV ;
}
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL ( st_nci_se_io ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
static void st_nci_se_wt_timeout ( unsigned long data )
2015-02-01 22:26:15 +01:00
{
/*
* No answer from the secure element
* within the defined timeout .
* Let ' s send a reset request as recovery procedure .
* According to the situation , we first try to send a software reset
* to the secure element . If the next command is still not
* answering in time , we send to the CLF a secure element hardware
* reset request .
*/
/* hardware reset managed through VCC_UICC_OUT power supply */
u8 param = 0x01 ;
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = ( struct st_nci_info * ) data ;
2015-02-01 22:26:15 +01:00
pr_debug ( " \n " ) ;
info - > se_info . bwi_active = false ;
if ( ! info - > se_info . xch_error ) {
info - > se_info . xch_error = true ;
2015-06-09 22:26:05 +02:00
nci_hci_send_event ( info - > ndlc - > ndev , ST_NCI_APDU_READER_GATE ,
ST_NCI_EVT_SE_SOFT_RESET , NULL , 0 ) ;
2015-02-01 22:26:15 +01:00
} else {
info - > se_info . xch_error = false ;
2015-06-09 22:26:05 +02:00
nci_hci_send_event ( info - > ndlc - > ndev , ST_NCI_DEVICE_MGNT_GATE ,
ST_NCI_EVT_SE_HARD_RESET , & param , 1 ) ;
2015-02-01 22:26:15 +01:00
}
info - > se_info . cb ( info - > se_info . cb_context , NULL , 0 , - ETIME ) ;
}
2015-06-09 22:26:05 +02:00
static void st_nci_se_activation_timeout ( unsigned long data )
2015-02-01 22:26:15 +01:00
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = ( struct st_nci_info * ) data ;
2015-02-01 22:26:15 +01:00
pr_debug ( " \n " ) ;
info - > se_info . se_active = false ;
complete ( & info - > se_info . req_completion ) ;
}
2015-10-25 22:54:39 +01:00
int st_nci_se_init ( struct nci_dev * ndev , struct st_nci_se_status * se_status )
2015-02-01 22:26:15 +01:00
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
init_completion ( & info - > se_info . req_completion ) ;
/* initialize timers */
init_timer ( & info - > se_info . bwi_timer ) ;
info - > se_info . bwi_timer . data = ( unsigned long ) info ;
2015-06-09 22:26:05 +02:00
info - > se_info . bwi_timer . function = st_nci_se_wt_timeout ;
2015-02-01 22:26:15 +01:00
info - > se_info . bwi_active = false ;
init_timer ( & info - > se_info . se_active_timer ) ;
info - > se_info . se_active_timer . data = ( unsigned long ) info ;
info - > se_info . se_active_timer . function =
2015-06-09 22:26:05 +02:00
st_nci_se_activation_timeout ;
2015-02-01 22:26:15 +01:00
info - > se_info . se_active = false ;
info - > se_info . xch_error = false ;
info - > se_info . wt_timeout =
2015-06-09 22:26:05 +02:00
ST_NCI_BWI_TO_TIMEOUT ( ST_NCI_ATR_DEFAULT_BWI ) ;
2015-02-01 22:26:15 +01:00
2015-10-25 22:54:39 +01:00
info - > se_info . se_status = se_status ;
2015-02-01 22:26:15 +01:00
return 0 ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL ( st_nci_se_init ) ;
2015-02-01 22:26:15 +01:00
2015-06-09 22:26:05 +02:00
void st_nci_se_deinit ( struct nci_dev * ndev )
2015-02-01 22:26:15 +01:00
{
2015-06-09 22:26:05 +02:00
struct st_nci_info * info = nci_get_drvdata ( ndev ) ;
2015-02-01 22:26:15 +01:00
if ( info - > se_info . bwi_active )
del_timer_sync ( & info - > se_info . bwi_timer ) ;
if ( info - > se_info . se_active )
del_timer_sync ( & info - > se_info . se_active_timer ) ;
info - > se_info . se_active = false ;
info - > se_info . bwi_active = false ;
}
2015-06-09 22:26:05 +02:00
EXPORT_SYMBOL ( st_nci_se_deinit ) ;
2015-02-01 22:26:15 +01:00