2015-10-22 12:11:42 +03:00
/* -------------------------------------------------------------------------
* Copyright ( C ) 2014 - 2016 , Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# include <linux/module.h>
# include <linux/nfc.h>
# include <linux/i2c.h>
# include <linux/delay.h>
# include <linux/firmware.h>
# include <net/nfc/nci_core.h>
# include "fdp.h"
# define FDP_OTP_PATCH_NAME "otp.bin"
# define FDP_RAM_PATCH_NAME "ram.bin"
# define FDP_FW_HEADER_SIZE 576
# define FDP_FW_UPDATE_SLEEP 1000
# define NCI_GET_VERSION_TIMEOUT 8000
# define NCI_PATCH_REQUEST_TIMEOUT 8000
# define FDP_PATCH_CONN_DEST 0xC2
# define FDP_PATCH_CONN_PARAM_TYPE 0xA0
# define NCI_PATCH_TYPE_RAM 0x00
# define NCI_PATCH_TYPE_OTP 0x01
# define NCI_PATCH_TYPE_EOT 0xFF
# define NCI_PARAM_ID_FW_RAM_VERSION 0xA0
# define NCI_PARAM_ID_FW_OTP_VERSION 0xA1
# define NCI_PARAM_ID_OTP_LIMITED_VERSION 0xC5
# define NCI_PARAM_ID_KEY_INDEX_ID 0xC6
# define NCI_GID_PROP 0x0F
# define NCI_OP_PROP_PATCH_OID 0x08
# define NCI_OP_PROP_SET_PDATA_OID 0x23
struct fdp_nci_info {
struct nfc_phy_ops * phy_ops ;
struct fdp_i2c_phy * phy ;
struct nci_dev * ndev ;
const struct firmware * otp_patch ;
const struct firmware * ram_patch ;
u32 otp_patch_version ;
u32 ram_patch_version ;
u32 otp_version ;
u32 ram_version ;
u32 limited_otp_version ;
u8 key_index ;
u8 * fw_vsc_cfg ;
u8 clock_type ;
u32 clock_freq ;
atomic_t data_pkt_counter ;
void ( * data_pkt_counter_cb ) ( struct nci_dev * ndev ) ;
u8 setup_patch_sent ;
u8 setup_patch_ntf ;
u8 setup_patch_status ;
u8 setup_reset_ntf ;
wait_queue_head_t setup_wq ;
} ;
static u8 nci_core_get_config_otp_ram_version [ 5 ] = {
0x04 ,
NCI_PARAM_ID_FW_RAM_VERSION ,
NCI_PARAM_ID_FW_OTP_VERSION ,
NCI_PARAM_ID_OTP_LIMITED_VERSION ,
NCI_PARAM_ID_KEY_INDEX_ID
} ;
struct nci_core_get_config_rsp {
u8 status ;
u8 count ;
u8 data [ 0 ] ;
} ;
static int fdp_nci_create_conn ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct core_conn_create_dest_spec_params param ;
int r ;
/* proprietary destination specific paramerer without value */
param . type = FDP_PATCH_CONN_PARAM_TYPE ;
param . length = 0x00 ;
r = nci_core_conn_create ( info - > ndev , FDP_PATCH_CONN_DEST , 1 ,
sizeof ( param ) , & param ) ;
if ( r )
return r ;
2016-04-30 09:12:51 +02:00
return nci_get_conn_info_by_dest_type_params ( ndev ,
FDP_PATCH_CONN_DEST , NULL ) ;
2015-10-22 12:11:42 +03:00
}
static inline int fdp_nci_get_versions ( struct nci_dev * ndev )
{
return nci_core_cmd ( ndev , NCI_OP_CORE_GET_CONFIG_CMD ,
sizeof ( nci_core_get_config_otp_ram_version ) ,
( __u8 * ) & nci_core_get_config_otp_ram_version ) ;
}
static inline int fdp_nci_patch_cmd ( struct nci_dev * ndev , u8 type )
{
return nci_prop_cmd ( ndev , NCI_OP_PROP_PATCH_OID , sizeof ( type ) , & type ) ;
}
static inline int fdp_nci_set_production_data ( struct nci_dev * ndev , u8 len ,
char * data )
{
return nci_prop_cmd ( ndev , NCI_OP_PROP_SET_PDATA_OID , len , data ) ;
}
static int fdp_nci_set_clock ( struct nci_dev * ndev , u8 clock_type ,
u32 clock_freq )
{
u32 fc = 13560 ;
u32 nd , num , delta ;
char data [ 9 ] ;
nd = ( 24 * fc ) / clock_freq ;
delta = 24 * fc - nd * clock_freq ;
num = ( 32768 * delta ) / clock_freq ;
data [ 0 ] = 0x00 ;
data [ 1 ] = 0x00 ;
data [ 2 ] = 0x00 ;
data [ 3 ] = 0x10 ;
data [ 4 ] = 0x04 ;
data [ 5 ] = num & 0xFF ;
data [ 6 ] = ( num > > 8 ) & 0xff ;
data [ 7 ] = nd ;
data [ 8 ] = clock_type ;
return fdp_nci_set_production_data ( ndev , 9 , data ) ;
}
static void fdp_nci_send_patch_cb ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
info - > setup_patch_sent = 1 ;
wake_up ( & info - > setup_wq ) ;
}
/**
* Register a packet sent counter and a callback
*
* We have no other way of knowing when all firmware packets were sent out
* on the i2c bus . We need to know that in order to close the connection and
* send the patch end message .
*/
static void fdp_nci_set_data_pkt_counter ( struct nci_dev * ndev ,
void ( * cb ) ( struct nci_dev * ndev ) , int count )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " NCI data pkt counter %d \n " , count ) ;
atomic_set ( & info - > data_pkt_counter , count ) ;
info - > data_pkt_counter_cb = cb ;
}
/**
* The device is expecting a stream of packets . All packets need to
* have the PBF flag set to 0x0 ( last packet ) even if the firmware
* file is segmented and there are multiple packets . If we give the
* whole firmware to nci_send_data it will segment it and it will set
* the PBF flag to 0x01 so we need to do the segmentation here .
*
* The firmware will be analyzed and applied when we send NCI_OP_PROP_PATCH_CMD
* command with NCI_PATCH_TYPE_EOT parameter . The device will send a
* NFCC_PATCH_NTF packaet and a NCI_OP_CORE_RESET_NTF packet .
*/
static int fdp_nci_send_patch ( struct nci_dev * ndev , u8 conn_id , u8 type )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
const struct firmware * fw ;
struct sk_buff * skb ;
unsigned long len ;
u8 max_size , payload_size ;
int rc = 0 ;
if ( ( type = = NCI_PATCH_TYPE_OTP & & ! info - > otp_patch ) | |
( type = = NCI_PATCH_TYPE_RAM & & ! info - > ram_patch ) )
return - EINVAL ;
if ( type = = NCI_PATCH_TYPE_OTP )
fw = info - > otp_patch ;
else
fw = info - > ram_patch ;
max_size = nci_conn_max_data_pkt_payload_size ( ndev , conn_id ) ;
if ( max_size < = 0 )
return - EINVAL ;
len = fw - > size ;
fdp_nci_set_data_pkt_counter ( ndev , fdp_nci_send_patch_cb ,
DIV_ROUND_UP ( fw - > size , max_size ) ) ;
while ( len ) {
payload_size = min_t ( unsigned long , ( unsigned long ) max_size ,
len ) ;
skb = nci_skb_alloc ( ndev , ( NCI_CTRL_HDR_SIZE + payload_size ) ,
GFP_KERNEL ) ;
if ( ! skb ) {
fdp_nci_set_data_pkt_counter ( ndev , NULL , 0 ) ;
return - ENOMEM ;
}
skb_reserve ( skb , NCI_CTRL_HDR_SIZE ) ;
memcpy ( skb_put ( skb , payload_size ) , fw - > data + ( fw - > size - len ) ,
payload_size ) ;
rc = nci_send_data ( ndev , conn_id , skb ) ;
if ( rc ) {
fdp_nci_set_data_pkt_counter ( ndev , NULL , 0 ) ;
return rc ;
}
len - = payload_size ;
}
return rc ;
}
static int fdp_nci_open ( struct nci_dev * ndev )
{
int r ;
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
r = info - > phy_ops - > enable ( info - > phy ) ;
return r ;
}
static int fdp_nci_close ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
return 0 ;
}
static int fdp_nci_send ( struct nci_dev * ndev , struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
if ( atomic_dec_and_test ( & info - > data_pkt_counter ) )
info - > data_pkt_counter_cb ( ndev ) ;
return info - > phy_ops - > write ( info - > phy , skb ) ;
}
int fdp_nci_recv_frame ( struct nci_dev * ndev , struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
return nci_recv_frame ( ndev , skb ) ;
}
EXPORT_SYMBOL ( fdp_nci_recv_frame ) ;
static int fdp_nci_request_firmware ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
u8 * data ;
int r ;
r = request_firmware ( & info - > ram_patch , FDP_RAM_PATCH_NAME , dev ) ;
if ( r < 0 ) {
nfc_err ( dev , " RAM patch request error \n " ) ;
goto error ;
}
data = ( u8 * ) info - > ram_patch - > data ;
info - > ram_patch_version =
data [ FDP_FW_HEADER_SIZE ] |
( data [ FDP_FW_HEADER_SIZE + 1 ] < < 8 ) |
( data [ FDP_FW_HEADER_SIZE + 2 ] < < 16 ) |
( data [ FDP_FW_HEADER_SIZE + 3 ] < < 24 ) ;
dev_dbg ( dev , " RAM patch version: %d, size: %d \n " ,
info - > ram_patch_version , ( int ) info - > ram_patch - > size ) ;
r = request_firmware ( & info - > otp_patch , FDP_OTP_PATCH_NAME , dev ) ;
if ( r < 0 ) {
nfc_err ( dev , " OTP patch request error \n " ) ;
goto out ;
}
data = ( u8 * ) info - > otp_patch - > data ;
info - > otp_patch_version =
data [ FDP_FW_HEADER_SIZE ] |
( data [ FDP_FW_HEADER_SIZE + 1 ] < < 8 ) |
( data [ FDP_FW_HEADER_SIZE + 2 ] < < 16 ) |
( data [ FDP_FW_HEADER_SIZE + 3 ] < < 24 ) ;
dev_dbg ( dev , " OTP patch version: %d, size: %d \n " ,
info - > otp_patch_version , ( int ) info - > otp_patch - > size ) ;
out :
return 0 ;
error :
return r ;
}
static void fdp_nci_release_firmware ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
if ( info - > otp_patch ) {
release_firmware ( info - > otp_patch ) ;
info - > otp_patch = NULL ;
}
if ( info - > ram_patch ) {
release_firmware ( info - > ram_patch ) ;
2016-03-22 23:17:00 +00:00
info - > ram_patch = NULL ;
2015-10-22 12:11:42 +03:00
}
}
static int fdp_nci_patch_otp ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
2016-06-05 11:17:10 +02:00
int conn_id ;
2015-10-22 12:11:42 +03:00
int r = 0 ;
if ( info - > otp_version > = info - > otp_patch_version )
goto out ;
info - > setup_patch_sent = 0 ;
info - > setup_reset_ntf = 0 ;
info - > setup_patch_ntf = 0 ;
/* Patch init request */
r = fdp_nci_patch_cmd ( ndev , NCI_PATCH_TYPE_OTP ) ;
if ( r )
goto out ;
/* Patch data connection creation */
conn_id = fdp_nci_create_conn ( ndev ) ;
if ( conn_id < 0 ) {
r = conn_id ;
goto out ;
}
/* Send the patch over the data connection */
r = fdp_nci_send_patch ( ndev , conn_id , NCI_PATCH_TYPE_OTP ) ;
if ( r )
goto out ;
/* Wait for all the packets to be send over i2c */
wait_event_interruptible ( info - > setup_wq ,
info - > setup_patch_sent = = 1 ) ;
/* make sure that the NFCC processed the last data packet */
msleep ( FDP_FW_UPDATE_SLEEP ) ;
/* Close the data connection */
r = nci_core_conn_close ( info - > ndev , conn_id ) ;
if ( r )
goto out ;
/* Patch finish message */
if ( fdp_nci_patch_cmd ( ndev , NCI_PATCH_TYPE_EOT ) ) {
nfc_err ( dev , " OTP patch error 0x%x \n " , r ) ;
r = - EINVAL ;
goto out ;
}
/* If the patch notification didn't arrive yet, wait for it */
wait_event_interruptible ( info - > setup_wq , info - > setup_patch_ntf ) ;
/* Check if the patching was successful */
r = info - > setup_patch_status ;
if ( r ) {
nfc_err ( dev , " OTP patch error 0x%x \n " , r ) ;
r = - EINVAL ;
goto out ;
}
/*
* We need to wait for the reset notification before we
* can continue
*/
wait_event_interruptible ( info - > setup_wq , info - > setup_reset_ntf ) ;
out :
return r ;
}
static int fdp_nci_patch_ram ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
2016-06-05 11:17:10 +02:00
int conn_id ;
2015-10-22 12:11:42 +03:00
int r = 0 ;
if ( info - > ram_version > = info - > ram_patch_version )
goto out ;
info - > setup_patch_sent = 0 ;
info - > setup_reset_ntf = 0 ;
info - > setup_patch_ntf = 0 ;
/* Patch init request */
r = fdp_nci_patch_cmd ( ndev , NCI_PATCH_TYPE_RAM ) ;
if ( r )
goto out ;
/* Patch data connection creation */
conn_id = fdp_nci_create_conn ( ndev ) ;
if ( conn_id < 0 ) {
r = conn_id ;
goto out ;
}
/* Send the patch over the data connection */
r = fdp_nci_send_patch ( ndev , conn_id , NCI_PATCH_TYPE_RAM ) ;
if ( r )
goto out ;
/* Wait for all the packets to be send over i2c */
wait_event_interruptible ( info - > setup_wq ,
info - > setup_patch_sent = = 1 ) ;
/* make sure that the NFCC processed the last data packet */
msleep ( FDP_FW_UPDATE_SLEEP ) ;
/* Close the data connection */
r = nci_core_conn_close ( info - > ndev , conn_id ) ;
if ( r )
goto out ;
/* Patch finish message */
if ( fdp_nci_patch_cmd ( ndev , NCI_PATCH_TYPE_EOT ) ) {
nfc_err ( dev , " RAM patch error 0x%x \n " , r ) ;
r = - EINVAL ;
goto out ;
}
/* If the patch notification didn't arrive yet, wait for it */
wait_event_interruptible ( info - > setup_wq , info - > setup_patch_ntf ) ;
/* Check if the patching was successful */
r = info - > setup_patch_status ;
if ( r ) {
nfc_err ( dev , " RAM patch error 0x%x \n " , r ) ;
r = - EINVAL ;
goto out ;
}
/*
* We need to wait for the reset notification before we
* can continue
*/
wait_event_interruptible ( info - > setup_wq , info - > setup_reset_ntf ) ;
out :
return r ;
}
static int fdp_nci_setup ( struct nci_dev * ndev )
{
/* Format: total length followed by an NCI packet */
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
int r ;
u8 patched = 0 ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
r = nci_core_init ( ndev ) ;
if ( r )
goto error ;
/* Get RAM and OTP version */
r = fdp_nci_get_versions ( ndev ) ;
if ( r )
goto error ;
/* Load firmware from disk */
r = fdp_nci_request_firmware ( ndev ) ;
if ( r )
goto error ;
/* Update OTP */
if ( info - > otp_version < info - > otp_patch_version ) {
r = fdp_nci_patch_otp ( ndev ) ;
if ( r )
goto error ;
patched = 1 ;
}
/* Update RAM */
if ( info - > ram_version < info - > ram_patch_version ) {
r = fdp_nci_patch_ram ( ndev ) ;
if ( r )
goto error ;
patched = 1 ;
}
/* Release the firmware buffers */
fdp_nci_release_firmware ( ndev ) ;
/* If a patch was applied the new version is checked */
if ( patched ) {
r = nci_core_init ( ndev ) ;
if ( r )
goto error ;
r = fdp_nci_get_versions ( ndev ) ;
if ( r )
goto error ;
if ( info - > otp_version ! = info - > otp_patch_version | |
info - > ram_version ! = info - > ram_patch_version ) {
nfc_err ( dev , " Firmware update failed " ) ;
r = - EINVAL ;
goto error ;
}
}
/*
* We initialized the devices but the NFC subsystem expects
* it to not be initialized .
*/
return nci_core_reset ( ndev ) ;
error :
fdp_nci_release_firmware ( ndev ) ;
nfc_err ( dev , " Setup error %d \n " , r ) ;
return r ;
}
static int fdp_nci_post_setup ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
int r ;
/* Check if the device has VSC */
if ( info - > fw_vsc_cfg & & info - > fw_vsc_cfg [ 0 ] ) {
/* Set the vendor specific configuration */
r = fdp_nci_set_production_data ( ndev , info - > fw_vsc_cfg [ 3 ] ,
& info - > fw_vsc_cfg [ 4 ] ) ;
if ( r ) {
nfc_err ( dev , " Vendor specific config set error %d \n " ,
r ) ;
return r ;
}
}
/* Set clock type and frequency */
r = fdp_nci_set_clock ( ndev , info - > clock_type , info - > clock_freq ) ;
if ( r ) {
nfc_err ( dev , " Clock set error %d \n " , r ) ;
return r ;
}
/*
* In order to apply the VSC FDP needs a reset
*/
r = nci_core_reset ( ndev ) ;
if ( r )
return r ;
/**
* The nci core was initialized when post setup was called
* so we leave it like that
*/
return nci_core_init ( ndev ) ;
}
static int fdp_nci_core_reset_ntf_packet ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
info - > setup_reset_ntf = 1 ;
wake_up ( & info - > setup_wq ) ;
return 0 ;
}
static int fdp_nci_prop_patch_ntf_packet ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
info - > setup_patch_ntf = 1 ;
info - > setup_patch_status = skb - > data [ 0 ] ;
wake_up ( & info - > setup_wq ) ;
return 0 ;
}
static int fdp_nci_prop_patch_rsp_packet ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
u8 status = skb - > data [ 0 ] ;
dev_dbg ( dev , " %s: status 0x%x \n " , __func__ , status ) ;
nci_req_complete ( ndev , status ) ;
return 0 ;
}
static int fdp_nci_prop_set_production_data_rsp_packet ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
u8 status = skb - > data [ 0 ] ;
dev_dbg ( dev , " %s: status 0x%x \n " , __func__ , status ) ;
nci_req_complete ( ndev , status ) ;
return 0 ;
}
static int fdp_nci_core_get_config_rsp_packet ( struct nci_dev * ndev ,
struct sk_buff * skb )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
struct nci_core_get_config_rsp * rsp = ( void * ) skb - > data ;
u8 i , * p ;
if ( rsp - > status = = NCI_STATUS_OK ) {
p = rsp - > data ;
for ( i = 0 ; i < 4 ; i + + ) {
switch ( * p + + ) {
case NCI_PARAM_ID_FW_RAM_VERSION :
p + + ;
info - > ram_version = le32_to_cpup ( ( __le32 * ) p ) ;
p + = 4 ;
break ;
case NCI_PARAM_ID_FW_OTP_VERSION :
p + + ;
info - > otp_version = le32_to_cpup ( ( __le32 * ) p ) ;
p + = 4 ;
break ;
case NCI_PARAM_ID_OTP_LIMITED_VERSION :
p + + ;
info - > otp_version = le32_to_cpup ( ( __le32 * ) p ) ;
p + = 4 ;
break ;
case NCI_PARAM_ID_KEY_INDEX_ID :
p + + ;
info - > key_index = * p + + ;
}
}
}
dev_dbg ( dev , " OTP version %d \n " , info - > otp_version ) ;
dev_dbg ( dev , " RAM version %d \n " , info - > ram_version ) ;
dev_dbg ( dev , " key index %d \n " , info - > key_index ) ;
dev_dbg ( dev , " %s: status 0x%x \n " , __func__ , rsp - > status ) ;
nci_req_complete ( ndev , rsp - > status ) ;
return 0 ;
}
static struct nci_driver_ops fdp_core_ops [ ] = {
{
. opcode = NCI_OP_CORE_GET_CONFIG_RSP ,
. rsp = fdp_nci_core_get_config_rsp_packet ,
} ,
{
. opcode = NCI_OP_CORE_RESET_NTF ,
. ntf = fdp_nci_core_reset_ntf_packet ,
} ,
} ;
static struct nci_driver_ops fdp_prop_ops [ ] = {
{
. opcode = nci_opcode_pack ( NCI_GID_PROP , NCI_OP_PROP_PATCH_OID ) ,
. rsp = fdp_nci_prop_patch_rsp_packet ,
. ntf = fdp_nci_prop_patch_ntf_packet ,
} ,
{
. opcode = nci_opcode_pack ( NCI_GID_PROP ,
NCI_OP_PROP_SET_PDATA_OID ) ,
. rsp = fdp_nci_prop_set_production_data_rsp_packet ,
} ,
} ;
struct nci_ops nci_ops = {
. open = fdp_nci_open ,
. close = fdp_nci_close ,
. send = fdp_nci_send ,
. setup = fdp_nci_setup ,
. post_setup = fdp_nci_post_setup ,
. prop_ops = fdp_prop_ops ,
. n_prop_ops = ARRAY_SIZE ( fdp_prop_ops ) ,
. core_ops = fdp_core_ops ,
. n_core_ops = ARRAY_SIZE ( fdp_core_ops ) ,
} ;
int fdp_nci_probe ( struct fdp_i2c_phy * phy , struct nfc_phy_ops * phy_ops ,
struct nci_dev * * ndevp , int tx_headroom ,
int tx_tailroom , u8 clock_type , u32 clock_freq ,
u8 * fw_vsc_cfg )
{
struct device * dev = & phy - > i2c_dev - > dev ;
struct fdp_nci_info * info ;
struct nci_dev * ndev ;
u32 protocols ;
int r ;
info = kzalloc ( sizeof ( struct fdp_nci_info ) , GFP_KERNEL ) ;
if ( ! info ) {
r = - ENOMEM ;
goto err_info_alloc ;
}
info - > phy = phy ;
info - > phy_ops = phy_ops ;
info - > clock_type = clock_type ;
info - > clock_freq = clock_freq ;
info - > fw_vsc_cfg = fw_vsc_cfg ;
init_waitqueue_head ( & info - > setup_wq ) ;
protocols = NFC_PROTO_JEWEL_MASK |
NFC_PROTO_MIFARE_MASK |
NFC_PROTO_FELICA_MASK |
NFC_PROTO_ISO14443_MASK |
NFC_PROTO_ISO14443_B_MASK |
NFC_PROTO_NFC_DEP_MASK |
NFC_PROTO_ISO15693_MASK ;
ndev = nci_allocate_device ( & nci_ops , protocols , tx_headroom ,
tx_tailroom ) ;
if ( ! ndev ) {
nfc_err ( dev , " Cannot allocate nfc ndev \n " ) ;
r = - ENOMEM ;
goto err_alloc_ndev ;
}
r = nci_register_device ( ndev ) ;
if ( r )
goto err_regdev ;
* ndevp = ndev ;
info - > ndev = ndev ;
nci_set_drvdata ( ndev , info ) ;
return 0 ;
err_regdev :
nci_free_device ( ndev ) ;
err_alloc_ndev :
kfree ( info ) ;
err_info_alloc :
return r ;
}
EXPORT_SYMBOL ( fdp_nci_probe ) ;
void fdp_nci_remove ( struct nci_dev * ndev )
{
struct fdp_nci_info * info = nci_get_drvdata ( ndev ) ;
struct device * dev = & info - > phy - > i2c_dev - > dev ;
dev_dbg ( dev , " %s \n " , __func__ ) ;
nci_unregister_device ( ndev ) ;
nci_free_device ( ndev ) ;
kfree ( info ) ;
}
EXPORT_SYMBOL ( fdp_nci_remove ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " NFC NCI driver for Intel Fields Peak NFC controller " ) ;
MODULE_AUTHOR ( " Robert Dolca <robert.dolca@intel.com> " ) ;