2015-12-22 00:03:30 +01:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Driver for ST NFC Transceiver ST95HF
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( C ) 2015 STMicroelectronics Pvt . Ltd . 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/err.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/nfc.h>
# include <linux/of_gpio.h>
# include <linux/of.h>
# include <linux/of_irq.h>
# include <linux/property.h>
# include <linux/regulator/consumer.h>
# include <linux/wait.h>
# include <net/nfc/digital.h>
# include <net/nfc/nfc.h>
# include "spi.h"
/* supported protocols */
# define ST95HF_SUPPORTED_PROT (NFC_PROTO_ISO14443_MASK | \
NFC_PROTO_ISO14443_B_MASK | \
NFC_PROTO_ISO15693_MASK )
/* driver capabilities */
# define ST95HF_CAPABILITIES NFC_DIGITAL_DRV_CAPS_IN_CRC
/* Command Send Interface */
/* ST95HF_COMMAND_SEND CMD Ids */
# define ECHO_CMD 0x55
# define WRITE_REGISTER_CMD 0x9
# define PROTOCOL_SELECT_CMD 0x2
# define SEND_RECEIVE_CMD 0x4
/* Select protocol codes */
# define ISO15693_PROTOCOL_CODE 0x1
# define ISO14443A_PROTOCOL_CODE 0x2
# define ISO14443B_PROTOCOL_CODE 0x3
/*
* head room len is 3
* 1 byte for control byte
* 1 byte for cmd
* 1 byte for size
*/
# define ST95HF_HEADROOM_LEN 3
/*
* tailroom is 1 for ISO14443A
* and 0 for ISO14443B / ISO15693 ,
* hence the max value 1 should be
* taken .
*/
# define ST95HF_TAILROOM_LEN 1
/* Command Response interface */
# define MAX_RESPONSE_BUFFER_SIZE 280
# define ECHORESPONSE 0x55
# define ST95HF_ERR_MASK 0xF
# define ST95HF_TIMEOUT_ERROR 0x87
# define ST95HF_NFCA_CRC_ERR_MASK 0x20
# define ST95HF_NFCB_CRC_ERR_MASK 0x01
/* ST95HF transmission flag values */
# define TRFLAG_NFCA_SHORT_FRAME 0x07
# define TRFLAG_NFCA_STD_FRAME 0x08
# define TRFLAG_NFCA_STD_FRAME_CRC 0x28
/* Misc defs */
# define HIGH 1
# define LOW 0
# define ISO14443A_RATS_REQ 0xE0
# define RATS_TB1_PRESENT_MASK 0x20
# define RATS_TA1_PRESENT_MASK 0x10
# define TB1_FWI_MASK 0xF0
# define WTX_REQ_FROM_TAG 0xF2
# define MAX_CMD_LEN 0x7
# define MAX_CMD_PARAMS 4
struct cmd {
int cmd_len ;
unsigned char cmd_id ;
unsigned char no_cmd_params ;
unsigned char cmd_params [ MAX_CMD_PARAMS ] ;
enum req_type req ;
} ;
struct param_list {
int param_offset ;
int new_param_val ;
} ;
/*
* List of top - level cmds to be used internally by the driver .
* All these commands are build on top of ST95HF basic commands
* such as SEND_RECEIVE_CMD , PROTOCOL_SELECT_CMD , etc .
* These top level cmds are used internally while implementing various ops of
* digital layer / driver probe or extending the digital framework layer for
* features that are not yet implemented there , for example , WTX cmd handling .
*/
enum st95hf_cmd_list {
CMD_ECHO ,
CMD_ISO14443A_CONFIG ,
CMD_ISO14443A_DEMOGAIN ,
CMD_ISO14443B_DEMOGAIN ,
CMD_ISO14443A_PROTOCOL_SELECT ,
CMD_ISO14443B_PROTOCOL_SELECT ,
CMD_WTX_RESPONSE ,
CMD_FIELD_OFF ,
CMD_ISO15693_PROTOCOL_SELECT ,
} ;
static const struct cmd cmd_array [ ] = {
[ CMD_ECHO ] = {
. cmd_len = 0x2 ,
. cmd_id = ECHO_CMD ,
. no_cmd_params = 0 ,
. req = SYNC ,
} ,
[ CMD_ISO14443A_CONFIG ] = {
. cmd_len = 0x7 ,
. cmd_id = WRITE_REGISTER_CMD ,
. no_cmd_params = 0x4 ,
. cmd_params = { 0x3A , 0x00 , 0x5A , 0x04 } ,
. req = SYNC ,
} ,
[ CMD_ISO14443A_DEMOGAIN ] = {
. cmd_len = 0x7 ,
. cmd_id = WRITE_REGISTER_CMD ,
. no_cmd_params = 0x4 ,
. cmd_params = { 0x68 , 0x01 , 0x01 , 0xDF } ,
. req = SYNC ,
} ,
[ CMD_ISO14443B_DEMOGAIN ] = {
. cmd_len = 0x7 ,
. cmd_id = WRITE_REGISTER_CMD ,
. no_cmd_params = 0x4 ,
. cmd_params = { 0x68 , 0x01 , 0x01 , 0x51 } ,
. req = SYNC ,
} ,
[ CMD_ISO14443A_PROTOCOL_SELECT ] = {
. cmd_len = 0x7 ,
. cmd_id = PROTOCOL_SELECT_CMD ,
. no_cmd_params = 0x4 ,
. cmd_params = { ISO14443A_PROTOCOL_CODE , 0x00 , 0x01 , 0xA0 } ,
. req = SYNC ,
} ,
[ CMD_ISO14443B_PROTOCOL_SELECT ] = {
. cmd_len = 0x7 ,
. cmd_id = PROTOCOL_SELECT_CMD ,
. no_cmd_params = 0x4 ,
. cmd_params = { ISO14443B_PROTOCOL_CODE , 0x01 , 0x03 , 0xFF } ,
. req = SYNC ,
} ,
[ CMD_WTX_RESPONSE ] = {
. cmd_len = 0x6 ,
. cmd_id = SEND_RECEIVE_CMD ,
. no_cmd_params = 0x3 ,
. cmd_params = { 0xF2 , 0x00 , TRFLAG_NFCA_STD_FRAME_CRC } ,
. req = ASYNC ,
} ,
[ CMD_FIELD_OFF ] = {
. cmd_len = 0x5 ,
. cmd_id = PROTOCOL_SELECT_CMD ,
. no_cmd_params = 0x2 ,
. cmd_params = { 0x0 , 0x0 } ,
. req = SYNC ,
} ,
[ CMD_ISO15693_PROTOCOL_SELECT ] = {
. cmd_len = 0x5 ,
. cmd_id = PROTOCOL_SELECT_CMD ,
. no_cmd_params = 0x2 ,
. cmd_params = { ISO15693_PROTOCOL_CODE , 0x0D } ,
. req = SYNC ,
} ,
} ;
/* st95_digital_cmd_complete_arg stores client context */
struct st95_digital_cmd_complete_arg {
struct sk_buff * skb_resp ;
nfc_digital_cmd_complete_t complete_cb ;
void * cb_usrarg ;
bool rats ;
} ;
/*
* structure containing ST95HF driver specific data .
* @ spicontext : structure containing information required
* for spi communication between st95hf and host .
* @ ddev : nfc digital device object .
* @ nfcdev : nfc device object .
* @ enable_gpio : gpio used to enable st95hf transceiver .
* @ complete_cb_arg : structure to store various context information
* that is passed from nfc requesting thread to the threaded ISR .
* @ st95hf_supply : regulator " consumer " for NFC device .
* @ sendrcv_trflag : last byte of frame send by sendrecv command
* of st95hf . This byte contains transmission flag info .
* @ exchange_lock : semaphore used for signaling the st95hf_remove
* function that the last outstanding async nfc request is finished .
* @ rm_lock : mutex for ensuring safe access of nfc digital object
* from threaded ISR . Usage of this mutex avoids any race between
* deletion of the object from st95hf_remove ( ) and its access from
* the threaded ISR .
* @ nfcdev_free : flag to have the state of nfc device object .
* [ alive | died ]
* @ current_protocol : current nfc protocol .
* @ current_rf_tech : current rf technology .
* @ fwi : frame waiting index , received in reply of RATS according to
* digital protocol .
*/
struct st95hf_context {
struct st95hf_spi_context spicontext ;
struct nfc_digital_dev * ddev ;
struct nfc_dev * nfcdev ;
unsigned int enable_gpio ;
struct st95_digital_cmd_complete_arg complete_cb_arg ;
struct regulator * st95hf_supply ;
unsigned char sendrcv_trflag ;
struct semaphore exchange_lock ;
struct mutex rm_lock ;
bool nfcdev_free ;
u8 current_protocol ;
u8 current_rf_tech ;
int fwi ;
} ;
/*
* st95hf_send_recv_cmd ( ) is for sending commands to ST95HF
* that are described in the cmd_array [ ] . It can optionally
* receive the response if the cmd request is of type
* SYNC . For that to happen caller must pass true to recv_res .
* For ASYNC request , recv_res is ignored and the
* function will never try to receive the response on behalf
* of the caller .
*/
static int st95hf_send_recv_cmd ( struct st95hf_context * st95context ,
enum st95hf_cmd_list cmd ,
int no_modif ,
struct param_list * list_array ,
bool recv_res )
{
unsigned char spi_cmd_buffer [ MAX_CMD_LEN ] ;
int i , ret ;
struct device * dev = & st95context - > spicontext . spidev - > dev ;
if ( cmd_array [ cmd ] . cmd_len > MAX_CMD_LEN )
return - EINVAL ;
if ( cmd_array [ cmd ] . no_cmd_params < no_modif )
return - EINVAL ;
if ( no_modif & & ! list_array )
return - EINVAL ;
spi_cmd_buffer [ 0 ] = ST95HF_COMMAND_SEND ;
spi_cmd_buffer [ 1 ] = cmd_array [ cmd ] . cmd_id ;
spi_cmd_buffer [ 2 ] = cmd_array [ cmd ] . no_cmd_params ;
memcpy ( & spi_cmd_buffer [ 3 ] , cmd_array [ cmd ] . cmd_params ,
spi_cmd_buffer [ 2 ] ) ;
for ( i = 0 ; i < no_modif ; i + + ) {
if ( list_array [ i ] . param_offset > = cmd_array [ cmd ] . no_cmd_params )
return - EINVAL ;
spi_cmd_buffer [ 3 + list_array [ i ] . param_offset ] =
list_array [ i ] . new_param_val ;
}
ret = st95hf_spi_send ( & st95context - > spicontext ,
spi_cmd_buffer ,
cmd_array [ cmd ] . cmd_len ,
cmd_array [ cmd ] . req ) ;
if ( ret ) {
dev_err ( dev , " st95hf_spi_send failed with error %d \n " , ret ) ;
return ret ;
}
if ( cmd_array [ cmd ] . req = = SYNC & & recv_res ) {
unsigned char st95hf_response_arr [ 2 ] ;
ret = st95hf_spi_recv_response ( & st95context - > spicontext ,
st95hf_response_arr ) ;
if ( ret < 0 ) {
dev_err ( dev , " spi error from st95hf_spi_recv_response(), err = 0x%x \n " ,
ret ) ;
return ret ;
}
if ( st95hf_response_arr [ 0 ] ) {
dev_err ( dev , " st95hf error from st95hf_spi_recv_response(), err = 0x%x \n " ,
st95hf_response_arr [ 0 ] ) ;
return - EIO ;
}
}
return 0 ;
}
static int st95hf_echo_command ( struct st95hf_context * st95context )
{
int result = 0 ;
unsigned char echo_response ;
result = st95hf_send_recv_cmd ( st95context , CMD_ECHO , 0 , NULL , false ) ;
if ( result )
return result ;
/* If control reached here, response can be taken */
result = st95hf_spi_recv_echo_res ( & st95context - > spicontext ,
& echo_response ) ;
if ( result ) {
dev_err ( & st95context - > spicontext . spidev - > dev ,
" err: echo response receieve error = 0x%x \n " , result ) ;
return result ;
}
if ( echo_response = = ECHORESPONSE )
return 0 ;
dev_err ( & st95context - > spicontext . spidev - > dev , " err: echo res is 0x%x \n " ,
echo_response ) ;
return - EIO ;
}
static int secondary_configuration_type4a ( struct st95hf_context * stcontext )
{
int result = 0 ;
struct device * dev = & stcontext - > nfcdev - > dev ;
/* 14443A config setting after select protocol */
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO14443A_CONFIG ,
0 ,
NULL ,
true ) ;
if ( result ) {
dev_err ( dev , " type a config cmd, err = 0x%x \n " , result ) ;
return result ;
}
/* 14443A demo gain setting */
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO14443A_DEMOGAIN ,
0 ,
NULL ,
true ) ;
if ( result )
dev_err ( dev , " type a demogain cmd, err = 0x%x \n " , result ) ;
return result ;
}
static int secondary_configuration_type4b ( struct st95hf_context * stcontext )
{
int result = 0 ;
struct device * dev = & stcontext - > nfcdev - > dev ;
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO14443B_DEMOGAIN ,
0 ,
NULL ,
true ) ;
if ( result )
dev_err ( dev , " type b demogain cmd, err = 0x%x \n " , result ) ;
return result ;
}
static int st95hf_select_protocol ( struct st95hf_context * stcontext , int type )
{
int result = 0 ;
struct device * dev ;
dev = & stcontext - > nfcdev - > dev ;
switch ( type ) {
case NFC_DIGITAL_RF_TECH_106A :
stcontext - > current_rf_tech = NFC_DIGITAL_RF_TECH_106A ;
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO14443A_PROTOCOL_SELECT ,
0 ,
NULL ,
true ) ;
if ( result ) {
dev_err ( dev , " protocol sel, err = 0x%x \n " ,
result ) ;
return result ;
}
/* secondary config. for 14443Type 4A after protocol select */
result = secondary_configuration_type4a ( stcontext ) ;
if ( result ) {
dev_err ( dev , " type a secondary config, err = 0x%x \n " ,
result ) ;
return result ;
}
break ;
case NFC_DIGITAL_RF_TECH_106B :
stcontext - > current_rf_tech = NFC_DIGITAL_RF_TECH_106B ;
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO14443B_PROTOCOL_SELECT ,
0 ,
NULL ,
true ) ;
if ( result ) {
dev_err ( dev , " protocol sel send, err = 0x%x \n " ,
result ) ;
return result ;
}
/*
* delay of 5 - 6 ms is required after select protocol
* command in case of ISO14443 Type B
*/
usleep_range ( 50000 , 60000 ) ;
/* secondary config. for 14443Type 4B after protocol select */
result = secondary_configuration_type4b ( stcontext ) ;
if ( result ) {
dev_err ( dev , " type b secondary config, err = 0x%x \n " ,
result ) ;
return result ;
}
break ;
case NFC_DIGITAL_RF_TECH_ISO15693 :
stcontext - > current_rf_tech = NFC_DIGITAL_RF_TECH_ISO15693 ;
result = st95hf_send_recv_cmd ( stcontext ,
CMD_ISO15693_PROTOCOL_SELECT ,
0 ,
NULL ,
true ) ;
if ( result ) {
dev_err ( dev , " protocol sel send, err = 0x%x \n " ,
result ) ;
return result ;
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static void st95hf_send_st95enable_negativepulse ( struct st95hf_context * st95con )
{
/* First make irq_in pin high */
gpio_set_value ( st95con - > enable_gpio , HIGH ) ;
/* wait for 1 milisecond */
usleep_range ( 1000 , 2000 ) ;
/* Make irq_in pin low */
gpio_set_value ( st95con - > enable_gpio , LOW ) ;
/* wait for minimum interrupt pulse to make st95 active */
usleep_range ( 1000 , 2000 ) ;
/* At end make it high */
gpio_set_value ( st95con - > enable_gpio , HIGH ) ;
}
/*
* Send a reset sequence over SPI bus ( Reset command + wait 3 ms +
* negative pulse on st95hf enable gpio
*/
static int st95hf_send_spi_reset_sequence ( struct st95hf_context * st95context )
{
int result = 0 ;
unsigned char reset_cmd = ST95HF_COMMAND_RESET ;
result = st95hf_spi_send ( & st95context - > spicontext ,
& reset_cmd ,
ST95HF_RESET_CMD_LEN ,
ASYNC ) ;
if ( result ) {
dev_err ( & st95context - > spicontext . spidev - > dev ,
" spi reset sequence cmd error = %d " , result ) ;
return result ;
}
/* wait for 3 milisecond to complete the controller reset process */
usleep_range ( 3000 , 4000 ) ;
/* send negative pulse to make st95hf active */
st95hf_send_st95enable_negativepulse ( st95context ) ;
/* wait for 10 milisecond : HFO setup time */
usleep_range ( 10000 , 20000 ) ;
return result ;
}
static int st95hf_por_sequence ( struct st95hf_context * st95context )
{
int nth_attempt = 1 ;
int result ;
st95hf_send_st95enable_negativepulse ( st95context ) ;
usleep_range ( 5000 , 6000 ) ;
do {
/* send an ECHO command and checks ST95HF response */
result = st95hf_echo_command ( st95context ) ;
dev_dbg ( & st95context - > spicontext . spidev - > dev ,
" response from echo function = 0x%x, attempt = %d \n " ,
result , nth_attempt ) ;
if ( ! result )
return 0 ;
/* send an pulse on IRQ in case of the chip is on sleep state */
if ( nth_attempt = = 2 )
st95hf_send_st95enable_negativepulse ( st95context ) ;
else
st95hf_send_spi_reset_sequence ( st95context ) ;
/* delay of 50 milisecond */
usleep_range ( 50000 , 51000 ) ;
} while ( nth_attempt + + < 3 ) ;
return - ETIMEDOUT ;
}
static int iso14443_config_fdt ( struct st95hf_context * st95context , int wtxm )
{
int result = 0 ;
struct device * dev = & st95context - > spicontext . spidev - > dev ;
struct nfc_digital_dev * nfcddev = st95context - > ddev ;
unsigned char pp_typeb ;
struct param_list new_params [ 2 ] ;
pp_typeb = cmd_array [ CMD_ISO14443B_PROTOCOL_SELECT ] . cmd_params [ 2 ] ;
if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443 & &
st95context - > fwi < 4 )
st95context - > fwi = 4 ;
new_params [ 0 ] . param_offset = 2 ;
if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443 )
new_params [ 0 ] . new_param_val = st95context - > fwi ;
else if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443_B )
new_params [ 0 ] . new_param_val = pp_typeb ;
new_params [ 1 ] . param_offset = 3 ;
new_params [ 1 ] . new_param_val = wtxm ;
switch ( nfcddev - > curr_protocol ) {
case NFC_PROTO_ISO14443 :
result = st95hf_send_recv_cmd ( st95context ,
CMD_ISO14443A_PROTOCOL_SELECT ,
2 ,
new_params ,
true ) ;
if ( result ) {
dev_err ( dev , " WTX type a sel proto, err = 0x%x \n " ,
result ) ;
return result ;
}
/* secondary config. for 14443Type 4A after protocol select */
result = secondary_configuration_type4a ( st95context ) ;
if ( result ) {
dev_err ( dev , " WTX type a second. config, err = 0x%x \n " ,
result ) ;
return result ;
}
break ;
case NFC_PROTO_ISO14443_B :
result = st95hf_send_recv_cmd ( st95context ,
CMD_ISO14443B_PROTOCOL_SELECT ,
2 ,
new_params ,
true ) ;
if ( result ) {
dev_err ( dev , " WTX type b sel proto, err = 0x%x \n " ,
result ) ;
return result ;
}
/* secondary config. for 14443Type 4B after protocol select */
result = secondary_configuration_type4b ( st95context ) ;
if ( result ) {
dev_err ( dev , " WTX type b second. config, err = 0x%x \n " ,
result ) ;
return result ;
}
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int st95hf_handle_wtx ( struct st95hf_context * stcontext ,
bool new_wtx ,
int wtx_val )
{
int result = 0 ;
unsigned char val_mm = 0 ;
struct param_list new_params [ 1 ] ;
struct nfc_digital_dev * nfcddev = stcontext - > ddev ;
struct device * dev = & stcontext - > nfcdev - > dev ;
if ( new_wtx ) {
result = iso14443_config_fdt ( stcontext , wtx_val & 0x3f ) ;
if ( result ) {
dev_err ( dev , " Config. setting error on WTX req, err = 0x%x \n " ,
result ) ;
return result ;
}
/* Send response of wtx with ASYNC as no response expected */
new_params [ 0 ] . param_offset = 1 ;
new_params [ 0 ] . new_param_val = wtx_val ;
result = st95hf_send_recv_cmd ( stcontext ,
CMD_WTX_RESPONSE ,
1 ,
new_params ,
false ) ;
if ( result )
dev_err ( dev , " WTX response send, err = 0x%x \n " , result ) ;
return result ;
}
/* if no new wtx, cofigure with default values */
if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443 )
val_mm = cmd_array [ CMD_ISO14443A_PROTOCOL_SELECT ] . cmd_params [ 3 ] ;
else if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443_B )
val_mm = cmd_array [ CMD_ISO14443B_PROTOCOL_SELECT ] . cmd_params [ 3 ] ;
result = iso14443_config_fdt ( stcontext , val_mm ) ;
if ( result )
dev_err ( dev , " Default config. setting error after WTX processing, err = 0x%x \n " ,
result ) ;
return result ;
}
static int st95hf_error_handling ( struct st95hf_context * stcontext ,
struct sk_buff * skb_resp ,
int res_len )
{
int result = 0 ;
unsigned char error_byte ;
struct device * dev = & stcontext - > nfcdev - > dev ;
/* First check ST95HF specific error */
if ( skb_resp - > data [ 0 ] & ST95HF_ERR_MASK ) {
if ( skb_resp - > data [ 0 ] = = ST95HF_TIMEOUT_ERROR )
result = - ETIMEDOUT ;
else
result = - EIO ;
return result ;
}
/* Check for CRC err only if CRC is present in the tag response */
switch ( stcontext - > current_rf_tech ) {
case NFC_DIGITAL_RF_TECH_106A :
if ( stcontext - > sendrcv_trflag = = TRFLAG_NFCA_STD_FRAME_CRC ) {
error_byte = skb_resp - > data [ res_len - 3 ] ;
if ( error_byte & ST95HF_NFCA_CRC_ERR_MASK ) {
/* CRC error occurred */
dev_err ( dev , " CRC error, byte received = 0x%x \n " ,
error_byte ) ;
result = - EIO ;
}
}
break ;
case NFC_DIGITAL_RF_TECH_106B :
case NFC_DIGITAL_RF_TECH_ISO15693 :
error_byte = skb_resp - > data [ res_len - 1 ] ;
if ( error_byte & ST95HF_NFCB_CRC_ERR_MASK ) {
/* CRC error occurred */
dev_err ( dev , " CRC error, byte received = 0x%x \n " ,
error_byte ) ;
result = - EIO ;
}
break ;
}
return result ;
}
static int st95hf_response_handler ( struct st95hf_context * stcontext ,
struct sk_buff * skb_resp ,
int res_len )
{
int result = 0 ;
int skb_len ;
unsigned char val_mm ;
struct nfc_digital_dev * nfcddev = stcontext - > ddev ;
struct device * dev = & stcontext - > nfcdev - > dev ;
struct st95_digital_cmd_complete_arg * cb_arg ;
cb_arg = & stcontext - > complete_cb_arg ;
/* Process the response */
skb_put ( skb_resp , res_len ) ;
/* Remove st95 header */
skb_pull ( skb_resp , 2 ) ;
skb_len = skb_resp - > len ;
/* check if it is case of RATS request reply & FWI is present */
if ( nfcddev - > curr_protocol = = NFC_PROTO_ISO14443 & & cb_arg - > rats & &
( skb_resp - > data [ 1 ] & RATS_TB1_PRESENT_MASK ) ) {
if ( skb_resp - > data [ 1 ] & RATS_TA1_PRESENT_MASK )
stcontext - > fwi =
( skb_resp - > data [ 3 ] & TB1_FWI_MASK ) > > 4 ;
else
stcontext - > fwi =
( skb_resp - > data [ 2 ] & TB1_FWI_MASK ) > > 4 ;
val_mm = cmd_array [ CMD_ISO14443A_PROTOCOL_SELECT ] . cmd_params [ 3 ] ;
result = iso14443_config_fdt ( stcontext , val_mm ) ;
if ( result ) {
dev_err ( dev , " error in config_fdt to handle fwi of ATS, error=%d \n " ,
result ) ;
return result ;
}
}
cb_arg - > rats = false ;
/* Remove CRC bytes only if received frames data has an eod (CRC) */
switch ( stcontext - > current_rf_tech ) {
case NFC_DIGITAL_RF_TECH_106A :
if ( stcontext - > sendrcv_trflag = = TRFLAG_NFCA_STD_FRAME_CRC )
skb_trim ( skb_resp , ( skb_len - 5 ) ) ;
else
skb_trim ( skb_resp , ( skb_len - 3 ) ) ;
break ;
case NFC_DIGITAL_RF_TECH_106B :
case NFC_DIGITAL_RF_TECH_ISO15693 :
skb_trim ( skb_resp , ( skb_len - 3 ) ) ;
break ;
}
return result ;
}
2015-12-22 04:53:37 -05:00
static irqreturn_t st95hf_irq_handler ( int irq , void * st95hfcontext )
2015-12-22 00:03:30 +01:00
{
struct st95hf_context * stcontext =
( struct st95hf_context * ) st95hfcontext ;
if ( stcontext - > spicontext . req_issync ) {
complete ( & stcontext - > spicontext . done ) ;
stcontext - > spicontext . req_issync = false ;
return IRQ_HANDLED ;
}
return IRQ_WAKE_THREAD ;
}
2015-12-22 04:53:37 -05:00
static irqreturn_t st95hf_irq_thread_handler ( int irq , void * st95hfcontext )
2015-12-22 00:03:30 +01:00
{
int result = 0 ;
int res_len ;
static bool wtx ;
struct device * dev ;
struct device * spidevice ;
struct nfc_digital_dev * nfcddev ;
struct sk_buff * skb_resp ;
struct st95hf_context * stcontext =
( struct st95hf_context * ) st95hfcontext ;
struct st95_digital_cmd_complete_arg * cb_arg ;
spidevice = & stcontext - > spicontext . spidev - > dev ;
/*
* check semaphore , if not down ( ) already , then we don ' t
* know in which context the ISR is called and surely it
* will be a bug . Note that down ( ) of the semaphore is done
* in the corresponding st95hf_in_send_cmd ( ) and then
* only this ISR should be called . ISR will up ( ) the
* semaphore before leaving . Hence when the ISR is called
* the correct behaviour is down_trylock ( ) should always
* return 1 ( indicating semaphore cant be taken and hence no
* change in semaphore count ) .
* If not , then we up ( ) the semaphore and crash on
* a BUG ( ) !
*/
if ( ! down_trylock ( & stcontext - > exchange_lock ) ) {
up ( & stcontext - > exchange_lock ) ;
WARN ( 1 , " unknown context in ST95HF ISR " ) ;
return IRQ_NONE ;
}
cb_arg = & stcontext - > complete_cb_arg ;
skb_resp = cb_arg - > skb_resp ;
mutex_lock ( & stcontext - > rm_lock ) ;
res_len = st95hf_spi_recv_response ( & stcontext - > spicontext ,
skb_resp - > data ) ;
if ( res_len < 0 ) {
dev_err ( spidevice , " TISR spi response err = 0x%x \n " , res_len ) ;
result = res_len ;
goto end ;
}
/* if stcontext->nfcdev_free is true, it means remove already ran */
if ( stcontext - > nfcdev_free ) {
result = - ENODEV ;
goto end ;
}
dev = & stcontext - > nfcdev - > dev ;
nfcddev = stcontext - > ddev ;
if ( skb_resp - > data [ 2 ] = = WTX_REQ_FROM_TAG ) {
/* Request for new FWT from tag */
result = st95hf_handle_wtx ( stcontext , true , skb_resp - > data [ 3 ] ) ;
if ( result )
goto end ;
wtx = true ;
mutex_unlock ( & stcontext - > rm_lock ) ;
return IRQ_HANDLED ;
}
result = st95hf_error_handling ( stcontext , skb_resp , res_len ) ;
if ( result )
goto end ;
result = st95hf_response_handler ( stcontext , skb_resp , res_len ) ;
if ( result )
goto end ;
/*
* If select protocol is done on wtx req . do select protocol
* again with default values
*/
if ( wtx ) {
wtx = false ;
result = st95hf_handle_wtx ( stcontext , false , 0 ) ;
if ( result )
goto end ;
}
/* call digital layer callback */
cb_arg - > complete_cb ( stcontext - > ddev , cb_arg - > cb_usrarg , skb_resp ) ;
/* up the semaphore before returning */
up ( & stcontext - > exchange_lock ) ;
mutex_unlock ( & stcontext - > rm_lock ) ;
return IRQ_HANDLED ;
end :
kfree_skb ( skb_resp ) ;
wtx = false ;
cb_arg - > rats = false ;
skb_resp = ERR_PTR ( result ) ;
/* call of callback with error */
cb_arg - > complete_cb ( stcontext - > ddev , cb_arg - > cb_usrarg , skb_resp ) ;
/* up the semaphore before returning */
up ( & stcontext - > exchange_lock ) ;
mutex_unlock ( & stcontext - > rm_lock ) ;
return IRQ_HANDLED ;
}
/* NFC ops functions definition */
static int st95hf_in_configure_hw ( struct nfc_digital_dev * ddev ,
int type ,
int param )
{
struct st95hf_context * stcontext = nfc_digital_get_drvdata ( ddev ) ;
if ( type = = NFC_DIGITAL_CONFIG_RF_TECH )
return st95hf_select_protocol ( stcontext , param ) ;
if ( type = = NFC_DIGITAL_CONFIG_FRAMING ) {
switch ( param ) {
case NFC_DIGITAL_FRAMING_NFCA_SHORT :
stcontext - > sendrcv_trflag = TRFLAG_NFCA_SHORT_FRAME ;
break ;
case NFC_DIGITAL_FRAMING_NFCA_STANDARD :
stcontext - > sendrcv_trflag = TRFLAG_NFCA_STD_FRAME ;
break ;
case NFC_DIGITAL_FRAMING_NFCA_T4T :
case NFC_DIGITAL_FRAMING_NFCA_NFC_DEP :
case NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A :
stcontext - > sendrcv_trflag = TRFLAG_NFCA_STD_FRAME_CRC ;
break ;
case NFC_DIGITAL_FRAMING_NFCB :
case NFC_DIGITAL_FRAMING_ISO15693_INVENTORY :
case NFC_DIGITAL_FRAMING_ISO15693_T5T :
break ;
}
}
return 0 ;
}
static int rf_off ( struct st95hf_context * stcontext )
{
int rc ;
struct device * dev ;
dev = & stcontext - > nfcdev - > dev ;
rc = st95hf_send_recv_cmd ( stcontext , CMD_FIELD_OFF , 0 , NULL , true ) ;
if ( rc )
dev_err ( dev , " protocol sel send field off, err = 0x%x \n " , rc ) ;
return rc ;
}
static int st95hf_in_send_cmd ( struct nfc_digital_dev * ddev ,
struct sk_buff * skb ,
u16 timeout ,
nfc_digital_cmd_complete_t cb ,
void * arg )
{
struct st95hf_context * stcontext = nfc_digital_get_drvdata ( ddev ) ;
int rc ;
struct sk_buff * skb_resp ;
int len_data_to_tag = 0 ;
skb_resp = nfc_alloc_recv_skb ( MAX_RESPONSE_BUFFER_SIZE , GFP_KERNEL ) ;
if ( ! skb_resp ) {
rc = - ENOMEM ;
goto error ;
}
switch ( stcontext - > current_rf_tech ) {
case NFC_DIGITAL_RF_TECH_106A :
len_data_to_tag = skb - > len + 1 ;
networking: add and use skb_put_u8()
Joe and Bjørn suggested that it'd be nicer to not have the
cast in the fairly common case of doing
*(u8 *)skb_put(skb, 1) = c;
Add skb_put_u8() for this case, and use it across the code,
using the following spatch:
@@
expression SKB, C, S;
typedef u8;
identifier fn = {skb_put};
fresh identifier fn2 = fn ## "_u8";
@@
- *(u8 *)fn(SKB, S) = C;
+ fn2(SKB, C);
Note that due to the "S", the spatch isn't perfect, it should
have checked that S is 1, but there's also places that use a
sizeof expression like sizeof(var) or sizeof(u8) etc. Turns
out that nobody ever did something like
*(u8 *)skb_put(skb, 2) = c;
which would be wrong anyway since the second byte wouldn't be
initialized.
Suggested-by: Joe Perches <joe@perches.com>
Suggested-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 14:29:24 +02:00
skb_put_u8 ( skb , stcontext - > sendrcv_trflag ) ;
2015-12-22 00:03:30 +01:00
break ;
case NFC_DIGITAL_RF_TECH_106B :
case NFC_DIGITAL_RF_TECH_ISO15693 :
len_data_to_tag = skb - > len ;
break ;
default :
rc = - EINVAL ;
goto free_skb_resp ;
}
skb_push ( skb , 3 ) ;
skb - > data [ 0 ] = ST95HF_COMMAND_SEND ;
skb - > data [ 1 ] = SEND_RECEIVE_CMD ;
skb - > data [ 2 ] = len_data_to_tag ;
stcontext - > complete_cb_arg . skb_resp = skb_resp ;
stcontext - > complete_cb_arg . cb_usrarg = arg ;
stcontext - > complete_cb_arg . complete_cb = cb ;
if ( ( skb - > data [ 3 ] = = ISO14443A_RATS_REQ ) & &
ddev - > curr_protocol = = NFC_PROTO_ISO14443 )
stcontext - > complete_cb_arg . rats = true ;
/*
* down the semaphore to indicate to remove func that an
* ISR is pending , note that it will not block here in any case .
* If found blocked , it is a BUG !
*/
rc = down_killable ( & stcontext - > exchange_lock ) ;
if ( rc ) {
WARN ( 1 , " Semaphore is not found up in st95hf_in_send_cmd \n " ) ;
return rc ;
}
rc = st95hf_spi_send ( & stcontext - > spicontext , skb - > data ,
skb - > len ,
ASYNC ) ;
if ( rc ) {
dev_err ( & stcontext - > nfcdev - > dev ,
" Error %d trying to perform data_exchange " , rc ) ;
/* up the semaphore since ISR will never come in this case */
up ( & stcontext - > exchange_lock ) ;
goto free_skb_resp ;
}
kfree_skb ( skb ) ;
return rc ;
free_skb_resp :
kfree_skb ( skb_resp ) ;
error :
return rc ;
}
/* p2p will be supported in a later release ! */
static int st95hf_tg_configure_hw ( struct nfc_digital_dev * ddev ,
int type ,
int param )
{
return 0 ;
}
static int st95hf_tg_send_cmd ( struct nfc_digital_dev * ddev ,
struct sk_buff * skb ,
u16 timeout ,
nfc_digital_cmd_complete_t cb ,
void * arg )
{
return 0 ;
}
static int st95hf_tg_listen ( struct nfc_digital_dev * ddev ,
u16 timeout ,
nfc_digital_cmd_complete_t cb ,
void * arg )
{
return 0 ;
}
static int st95hf_tg_get_rf_tech ( struct nfc_digital_dev * ddev , u8 * rf_tech )
{
return 0 ;
}
static int st95hf_switch_rf ( struct nfc_digital_dev * ddev , bool on )
{
u8 rf_tech ;
struct st95hf_context * stcontext = nfc_digital_get_drvdata ( ddev ) ;
rf_tech = ddev - > curr_rf_tech ;
if ( on )
/* switch on RF field */
return st95hf_select_protocol ( stcontext , rf_tech ) ;
/* switch OFF RF field */
return rf_off ( stcontext ) ;
}
/* TODO st95hf_abort_cmd */
static void st95hf_abort_cmd ( struct nfc_digital_dev * ddev )
{
}
static struct nfc_digital_ops st95hf_nfc_digital_ops = {
. in_configure_hw = st95hf_in_configure_hw ,
. in_send_cmd = st95hf_in_send_cmd ,
. tg_listen = st95hf_tg_listen ,
. tg_configure_hw = st95hf_tg_configure_hw ,
. tg_send_cmd = st95hf_tg_send_cmd ,
. tg_get_rf_tech = st95hf_tg_get_rf_tech ,
. switch_rf = st95hf_switch_rf ,
. abort_cmd = st95hf_abort_cmd ,
} ;
static const struct spi_device_id st95hf_id [ ] = {
{ " st95hf " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , st95hf_id ) ;
static int st95hf_probe ( struct spi_device * nfc_spi_dev )
{
int ret ;
struct st95hf_context * st95context ;
struct st95hf_spi_context * spicontext ;
nfc_info ( & nfc_spi_dev - > dev , " ST95HF driver probe called. \n " ) ;
st95context = devm_kzalloc ( & nfc_spi_dev - > dev ,
sizeof ( struct st95hf_context ) ,
GFP_KERNEL ) ;
if ( ! st95context )
return - ENOMEM ;
spicontext = & st95context - > spicontext ;
spicontext - > spidev = nfc_spi_dev ;
st95context - > fwi =
cmd_array [ CMD_ISO14443A_PROTOCOL_SELECT ] . cmd_params [ 2 ] ;
if ( device_property_present ( & nfc_spi_dev - > dev , " st95hfvin " ) ) {
st95context - > st95hf_supply =
devm_regulator_get ( & nfc_spi_dev - > dev ,
" st95hfvin " ) ;
if ( IS_ERR ( st95context - > st95hf_supply ) ) {
dev_err ( & nfc_spi_dev - > dev , " failed to acquire regulator \n " ) ;
return PTR_ERR ( st95context - > st95hf_supply ) ;
}
ret = regulator_enable ( st95context - > st95hf_supply ) ;
if ( ret ) {
dev_err ( & nfc_spi_dev - > dev , " failed to enable regulator \n " ) ;
return ret ;
}
}
init_completion ( & spicontext - > done ) ;
mutex_init ( & spicontext - > spi_lock ) ;
/*
* Store spicontext in spi device object for using it in
* remove function
*/
dev_set_drvdata ( & nfc_spi_dev - > dev , spicontext ) ;
st95context - > enable_gpio =
of_get_named_gpio ( nfc_spi_dev - > dev . of_node ,
" enable-gpio " ,
0 ) ;
if ( ! gpio_is_valid ( st95context - > enable_gpio ) ) {
dev_err ( & nfc_spi_dev - > dev , " No valid enable gpio \n " ) ;
ret = st95context - > enable_gpio ;
goto err_disable_regulator ;
}
ret = devm_gpio_request_one ( & nfc_spi_dev - > dev , st95context - > enable_gpio ,
GPIOF_DIR_OUT | GPIOF_INIT_HIGH ,
" enable_gpio " ) ;
if ( ret )
goto err_disable_regulator ;
if ( nfc_spi_dev - > irq > 0 ) {
if ( devm_request_threaded_irq ( & nfc_spi_dev - > dev ,
nfc_spi_dev - > irq ,
2015-12-22 04:53:37 -05:00
st95hf_irq_handler ,
st95hf_irq_thread_handler ,
2015-12-22 00:03:30 +01:00
IRQF_TRIGGER_FALLING ,
" st95hf " ,
( void * ) st95context ) < 0 ) {
dev_err ( & nfc_spi_dev - > dev , " err: irq request for st95hf is failed \n " ) ;
ret = - EINVAL ;
goto err_disable_regulator ;
}
} else {
dev_err ( & nfc_spi_dev - > dev , " not a valid IRQ associated with ST95HF \n " ) ;
ret = - EINVAL ;
goto err_disable_regulator ;
}
/*
* First reset SPI to handle warm reset of the system .
* It will put the ST95HF device in Power ON state
* which make the state of device identical to state
* at the time of cold reset of the system .
*/
ret = st95hf_send_spi_reset_sequence ( st95context ) ;
if ( ret ) {
dev_err ( & nfc_spi_dev - > dev , " err: spi_reset_sequence failed \n " ) ;
goto err_disable_regulator ;
}
/* call PowerOnReset sequence of ST95hf to activate it */
ret = st95hf_por_sequence ( st95context ) ;
if ( ret ) {
dev_err ( & nfc_spi_dev - > dev , " err: por seq failed for st95hf \n " ) ;
goto err_disable_regulator ;
}
/* create NFC dev object and register with NFC Subsystem */
st95context - > ddev = nfc_digital_allocate_device ( & st95hf_nfc_digital_ops ,
ST95HF_SUPPORTED_PROT ,
ST95HF_CAPABILITIES ,
ST95HF_HEADROOM_LEN ,
ST95HF_TAILROOM_LEN ) ;
if ( ! st95context - > ddev ) {
ret = - ENOMEM ;
goto err_disable_regulator ;
}
st95context - > nfcdev = st95context - > ddev - > nfc_dev ;
nfc_digital_set_parent_dev ( st95context - > ddev , & nfc_spi_dev - > dev ) ;
ret = nfc_digital_register_device ( st95context - > ddev ) ;
if ( ret ) {
dev_err ( & st95context - > nfcdev - > dev , " st95hf registration failed \n " ) ;
goto err_free_digital_device ;
}
/* store st95context in nfc device object */
nfc_digital_set_drvdata ( st95context - > ddev , st95context ) ;
sema_init ( & st95context - > exchange_lock , 1 ) ;
mutex_init ( & st95context - > rm_lock ) ;
return ret ;
err_free_digital_device :
nfc_digital_free_device ( st95context - > ddev ) ;
err_disable_regulator :
if ( st95context - > st95hf_supply )
regulator_disable ( st95context - > st95hf_supply ) ;
return ret ;
}
static int st95hf_remove ( struct spi_device * nfc_spi_dev )
{
int result = 0 ;
unsigned char reset_cmd = ST95HF_COMMAND_RESET ;
struct st95hf_spi_context * spictx = dev_get_drvdata ( & nfc_spi_dev - > dev ) ;
struct st95hf_context * stcontext = container_of ( spictx ,
struct st95hf_context ,
spicontext ) ;
mutex_lock ( & stcontext - > rm_lock ) ;
nfc_digital_unregister_device ( stcontext - > ddev ) ;
nfc_digital_free_device ( stcontext - > ddev ) ;
stcontext - > nfcdev_free = true ;
mutex_unlock ( & stcontext - > rm_lock ) ;
/* if last in_send_cmd's ISR is pending, wait for it to finish */
result = down_killable ( & stcontext - > exchange_lock ) ;
if ( result = = - EINTR )
dev_err ( & spictx - > spidev - > dev , " sleep for semaphore interrupted by signal \n " ) ;
/* next reset the ST95HF controller */
result = st95hf_spi_send ( & stcontext - > spicontext ,
& reset_cmd ,
ST95HF_RESET_CMD_LEN ,
ASYNC ) ;
if ( result ) {
dev_err ( & spictx - > spidev - > dev ,
" ST95HF reset failed in remove() err = %d \n " , result ) ;
return result ;
}
/* wait for 3 ms to complete the controller reset process */
usleep_range ( 3000 , 4000 ) ;
/* disable regulator */
if ( stcontext - > st95hf_supply )
regulator_disable ( stcontext - > st95hf_supply ) ;
return result ;
}
/* Register as SPI protocol driver */
static struct spi_driver st95hf_driver = {
. driver = {
. name = " st95hf " ,
. owner = THIS_MODULE ,
} ,
. id_table = st95hf_id ,
. probe = st95hf_probe ,
. remove = st95hf_remove ,
} ;
module_spi_driver ( st95hf_driver ) ;
MODULE_AUTHOR ( " Shikha Singh <shikha.singh@st.com> " ) ;
MODULE_DESCRIPTION ( " ST NFC Transceiver ST95HF driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;