2019-03-12 01:10:42 +03:00
// SPDX-License-Identifier: GPL-2.0
2013-04-15 13:19:20 +04:00
/*
2019-03-12 01:10:42 +03:00
* Copyright ( c ) 2013 , Intel Corporation .
2013-04-15 13:19:20 +04:00
*
2019-03-12 01:10:42 +03:00
* MEI Library for mei bus nfc device access
2013-04-15 13:19:20 +04:00
*/
2013-04-05 23:27:39 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2013-04-15 13:19:20 +04:00
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/nfc.h>
# include "mei_phy.h"
2015-05-07 15:54:06 +03:00
struct mei_nfc_hdr {
u8 cmd ;
2015-05-07 15:54:04 +03:00
u8 status ;
u16 req_id ;
u32 reserved ;
u16 data_size ;
2015-05-07 15:54:06 +03:00
} __packed ;
struct mei_nfc_cmd {
struct mei_nfc_hdr hdr ;
2015-05-07 15:54:04 +03:00
u8 sub_command ;
u8 data [ ] ;
} __packed ;
struct mei_nfc_reply {
2015-05-07 15:54:06 +03:00
struct mei_nfc_hdr hdr ;
2015-05-07 15:54:04 +03:00
u8 sub_command ;
u8 reply_status ;
u8 data [ ] ;
} __packed ;
struct mei_nfc_if_version {
u8 radio_version_sw [ 3 ] ;
u8 reserved [ 3 ] ;
u8 radio_version_hw [ 3 ] ;
u8 i2c_addr ;
u8 fw_ivn ;
u8 vendor_id ;
u8 radio_type ;
} __packed ;
struct mei_nfc_connect {
u8 fw_ivn ;
u8 vendor_id ;
} __packed ;
struct mei_nfc_connect_resp {
u8 fw_ivn ;
u8 vendor_id ;
u16 me_major ;
u16 me_minor ;
u16 me_hotfix ;
u16 me_build ;
} __packed ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:04 +03:00
# define MEI_NFC_CMD_MAINTENANCE 0x00
# define MEI_NFC_CMD_HCI_SEND 0x01
# define MEI_NFC_CMD_HCI_RECV 0x02
# define MEI_NFC_SUBCMD_CONNECT 0x00
# define MEI_NFC_SUBCMD_IF_VERSION 0x01
2013-04-15 13:19:20 +04:00
# define MEI_NFC_MAX_READ (MEI_NFC_HEADER_SIZE + MEI_NFC_MAX_HCI_PAYLOAD)
# define MEI_DUMP_SKB_IN(info, skb) \
do { \
pr_debug ( " %s: \n " , info ) ; \
print_hex_dump_debug ( " mei in : " , DUMP_PREFIX_OFFSET , \
16 , 1 , ( skb ) - > data , ( skb ) - > len , false ) ; \
} while ( 0 )
# define MEI_DUMP_SKB_OUT(info, skb) \
do { \
pr_debug ( " %s: \n " , info ) ; \
print_hex_dump_debug ( " mei out: " , DUMP_PREFIX_OFFSET , \
2015-05-07 15:54:04 +03:00
16 , 1 , ( skb ) - > data , ( skb ) - > len , false ) ; \
2013-04-15 13:19:20 +04:00
} while ( 0 )
2015-05-07 15:54:06 +03:00
# define MEI_DUMP_NFC_HDR(info, _hdr) \
do { \
pr_debug ( " %s: \n " , info ) ; \
pr_debug ( " cmd=%02d status=%d req_id=%d rsvd=%d size=%d \n " , \
( _hdr ) - > cmd , ( _hdr ) - > status , ( _hdr ) - > req_id , \
( _hdr ) - > reserved , ( _hdr ) - > data_size ) ; \
} while ( 0 )
2015-05-07 15:54:04 +03:00
static int mei_nfc_if_version ( struct nfc_mei_phy * phy )
2013-04-15 13:19:20 +04:00
{
2015-05-07 15:54:04 +03:00
struct mei_nfc_cmd cmd ;
struct mei_nfc_reply * reply = NULL ;
struct mei_nfc_if_version * version ;
size_t if_version_length ;
int bytes_recv , r ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:04 +03:00
memset ( & cmd , 0 , sizeof ( struct mei_nfc_cmd ) ) ;
2015-05-07 15:54:06 +03:00
cmd . hdr . cmd = MEI_NFC_CMD_MAINTENANCE ;
cmd . hdr . data_size = 1 ;
2015-05-07 15:54:04 +03:00
cmd . sub_command = MEI_NFC_SUBCMD_IF_VERSION ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:06 +03:00
MEI_DUMP_NFC_HDR ( " version " , & cmd . hdr ) ;
2015-09-10 10:18:05 +03:00
r = mei_cldev_send ( phy - > cldev , ( u8 * ) & cmd , sizeof ( struct mei_nfc_cmd ) ) ;
2013-04-15 13:19:20 +04:00
if ( r < 0 ) {
2015-05-07 15:54:04 +03:00
pr_err ( " Could not send IF version cmd \n " ) ;
2013-06-07 23:01:55 +04:00
return r ;
2013-04-15 13:19:20 +04:00
}
2015-05-07 15:54:04 +03:00
/* to be sure on the stack we alloc memory */
if_version_length = sizeof ( struct mei_nfc_reply ) +
sizeof ( struct mei_nfc_if_version ) ;
2013-05-01 01:48:50 +04:00
2015-05-07 15:54:04 +03:00
reply = kzalloc ( if_version_length , GFP_KERNEL ) ;
if ( ! reply )
return - ENOMEM ;
2015-09-10 10:18:05 +03:00
bytes_recv = mei_cldev_recv ( phy - > cldev , ( u8 * ) reply , if_version_length ) ;
2016-10-31 20:02:39 +03:00
if ( bytes_recv < 0 | | bytes_recv < if_version_length ) {
2015-05-07 15:54:04 +03:00
pr_err ( " Could not read IF version \n " ) ;
r = - EIO ;
goto err ;
2013-05-01 01:48:50 +04:00
}
2015-05-07 15:54:04 +03:00
version = ( struct mei_nfc_if_version * ) reply - > data ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:04 +03:00
phy - > fw_ivn = version - > fw_ivn ;
phy - > vendor_id = version - > vendor_id ;
phy - > radio_type = version - > radio_type ;
err :
kfree ( reply ) ;
return r ;
2013-04-15 13:19:20 +04:00
}
2015-05-07 15:54:04 +03:00
static int mei_nfc_connect ( struct nfc_mei_phy * phy )
2013-04-15 13:19:20 +04:00
{
2015-05-07 15:54:04 +03:00
struct mei_nfc_cmd * cmd , * reply ;
struct mei_nfc_connect * connect ;
struct mei_nfc_connect_resp * connect_resp ;
size_t connect_length , connect_resp_length ;
int bytes_recv , r ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:04 +03:00
connect_length = sizeof ( struct mei_nfc_cmd ) +
sizeof ( struct mei_nfc_connect ) ;
2013-04-15 13:19:20 +04:00
2015-05-07 15:54:04 +03:00
connect_resp_length = sizeof ( struct mei_nfc_cmd ) +
sizeof ( struct mei_nfc_connect_resp ) ;
cmd = kzalloc ( connect_length , GFP_KERNEL ) ;
if ( ! cmd )
return - ENOMEM ;
connect = ( struct mei_nfc_connect * ) cmd - > data ;
reply = kzalloc ( connect_resp_length , GFP_KERNEL ) ;
if ( ! reply ) {
kfree ( cmd ) ;
return - ENOMEM ;
}
connect_resp = ( struct mei_nfc_connect_resp * ) reply - > data ;
2015-05-07 15:54:06 +03:00
cmd - > hdr . cmd = MEI_NFC_CMD_MAINTENANCE ;
cmd - > hdr . data_size = 3 ;
2015-05-07 15:54:04 +03:00
cmd - > sub_command = MEI_NFC_SUBCMD_CONNECT ;
connect - > fw_ivn = phy - > fw_ivn ;
connect - > vendor_id = phy - > vendor_id ;
2015-05-07 15:54:06 +03:00
MEI_DUMP_NFC_HDR ( " connect request " , & cmd - > hdr ) ;
2015-09-10 10:18:05 +03:00
r = mei_cldev_send ( phy - > cldev , ( u8 * ) cmd , connect_length ) ;
2015-05-07 15:54:04 +03:00
if ( r < 0 ) {
pr_err ( " Could not send connect cmd %d \n " , r ) ;
goto err ;
}
2015-09-10 10:18:05 +03:00
bytes_recv = mei_cldev_recv ( phy - > cldev , ( u8 * ) reply ,
connect_resp_length ) ;
2015-05-07 15:54:04 +03:00
if ( bytes_recv < 0 ) {
r = bytes_recv ;
pr_err ( " Could not read connect response %d \n " , r ) ;
goto err ;
}
2015-05-07 15:54:06 +03:00
MEI_DUMP_NFC_HDR ( " connect reply " , & reply - > hdr ) ;
2015-05-07 15:54:04 +03:00
pr_info ( " IVN 0x%x Vendor ID 0x%x \n " ,
connect_resp - > fw_ivn , connect_resp - > vendor_id ) ;
pr_info ( " ME FW %d.%d.%d.%d \n " ,
connect_resp - > me_major , connect_resp - > me_minor ,
connect_resp - > me_hotfix , connect_resp - > me_build ) ;
r = 0 ;
err :
kfree ( reply ) ;
kfree ( cmd ) ;
return r ;
}
2021-07-29 13:40:12 +03:00
static int mei_nfc_send ( struct nfc_mei_phy * phy , const u8 * buf , size_t length )
2015-05-07 15:54:04 +03:00
{
2015-05-07 15:54:06 +03:00
struct mei_nfc_hdr * hdr ;
2015-05-07 15:54:04 +03:00
u8 * mei_buf ;
int err ;
err = - ENOMEM ;
mei_buf = kzalloc ( length + MEI_NFC_HEADER_SIZE , GFP_KERNEL ) ;
if ( ! mei_buf )
goto out ;
2015-05-07 15:54:06 +03:00
hdr = ( struct mei_nfc_hdr * ) mei_buf ;
2015-05-07 15:54:04 +03:00
hdr - > cmd = MEI_NFC_CMD_HCI_SEND ;
hdr - > status = 0 ;
hdr - > req_id = phy - > req_id ;
hdr - > reserved = 0 ;
hdr - > data_size = length ;
2015-05-07 15:54:06 +03:00
MEI_DUMP_NFC_HDR ( " send " , hdr ) ;
2015-05-07 15:54:04 +03:00
memcpy ( mei_buf + MEI_NFC_HEADER_SIZE , buf , length ) ;
2015-09-10 10:18:05 +03:00
err = mei_cldev_send ( phy - > cldev , mei_buf , length + MEI_NFC_HEADER_SIZE ) ;
2015-05-07 15:54:04 +03:00
if ( err < 0 )
goto out ;
if ( ! wait_event_interruptible_timeout ( phy - > send_wq ,
phy - > recv_req_id = = phy - > req_id , HZ ) ) {
pr_err ( " NFC MEI command timeout \n " ) ;
err = - ETIME ;
} else {
phy - > req_id + + ;
}
out :
kfree ( mei_buf ) ;
return err ;
2013-04-15 13:19:20 +04:00
}
/*
* Writing a frame must not return the number of written bytes .
* It must return either zero for success , or < 0 for error .
* In addition , it must not alter the skb
*/
static int nfc_mei_phy_write ( void * phy_id , struct sk_buff * skb )
{
struct nfc_mei_phy * phy = phy_id ;
int r ;
MEI_DUMP_SKB_OUT ( " mei frame sent " , skb ) ;
2015-05-07 15:54:04 +03:00
r = mei_nfc_send ( phy , skb - > data , skb - > len ) ;
2013-04-15 13:19:20 +04:00
if ( r > 0 )
r = 0 ;
return r ;
}
2015-05-07 15:54:04 +03:00
static int mei_nfc_recv ( struct nfc_mei_phy * phy , u8 * buf , size_t length )
{
2015-05-07 15:54:06 +03:00
struct mei_nfc_hdr * hdr ;
2015-05-07 15:54:04 +03:00
int received_length ;
2015-09-10 10:18:05 +03:00
received_length = mei_cldev_recv ( phy - > cldev , buf , length ) ;
2015-05-07 15:54:04 +03:00
if ( received_length < 0 )
return received_length ;
2015-05-07 15:54:06 +03:00
hdr = ( struct mei_nfc_hdr * ) buf ;
2015-05-07 15:54:04 +03:00
2015-05-07 15:54:06 +03:00
MEI_DUMP_NFC_HDR ( " receive " , hdr ) ;
if ( hdr - > cmd = = MEI_NFC_CMD_HCI_SEND ) {
phy - > recv_req_id = hdr - > req_id ;
2015-05-07 15:54:04 +03:00
wake_up ( & phy - > send_wq ) ;
return 0 ;
}
return received_length ;
}
2016-11-16 23:51:29 +03:00
static void nfc_mei_rx_cb ( struct mei_cl_device * cldev )
2013-04-15 13:19:20 +04:00
{
2016-10-19 16:33:30 +03:00
struct nfc_mei_phy * phy = mei_cldev_get_drvdata ( cldev ) ;
2016-11-16 23:51:29 +03:00
struct sk_buff * skb ;
int reply_size ;
2016-10-19 16:33:30 +03:00
if ( ! phy )
return ;
2013-04-15 13:19:20 +04:00
if ( phy - > hard_fault ! = 0 )
return ;
2016-11-16 23:51:29 +03:00
skb = alloc_skb ( MEI_NFC_MAX_READ , GFP_KERNEL ) ;
if ( ! skb )
return ;
2013-04-15 13:19:20 +04:00
2016-11-16 23:51:29 +03:00
reply_size = mei_nfc_recv ( phy , skb - > data , MEI_NFC_MAX_READ ) ;
if ( reply_size < MEI_NFC_HEADER_SIZE ) {
kfree_skb ( skb ) ;
return ;
}
2013-04-15 13:19:20 +04:00
2016-11-16 23:51:29 +03:00
skb_put ( skb , reply_size ) ;
skb_pull ( skb , MEI_NFC_HEADER_SIZE ) ;
2013-04-15 13:19:20 +04:00
2016-11-16 23:51:29 +03:00
MEI_DUMP_SKB_IN ( " mei frame read " , skb ) ;
2013-04-15 13:19:20 +04:00
2016-11-16 23:51:29 +03:00
nfc_hci_recv_frame ( phy - > hdev , skb ) ;
2013-04-15 13:19:20 +04:00
}
2015-05-07 15:54:04 +03:00
static int nfc_mei_phy_enable ( void * phy_id )
{
int r ;
struct nfc_mei_phy * phy = phy_id ;
if ( phy - > powered = = 1 )
return 0 ;
2015-09-10 10:18:05 +03:00
r = mei_cldev_enable ( phy - > cldev ) ;
2015-05-07 15:54:04 +03:00
if ( r < 0 ) {
pr_err ( " Could not enable device %d \n " , r ) ;
return r ;
}
r = mei_nfc_if_version ( phy ) ;
if ( r < 0 ) {
pr_err ( " Could not enable device %d \n " , r ) ;
goto err ;
}
r = mei_nfc_connect ( phy ) ;
if ( r < 0 ) {
pr_err ( " Could not connect to device %d \n " , r ) ;
goto err ;
}
2016-11-16 23:51:29 +03:00
r = mei_cldev_register_rx_cb ( phy - > cldev , nfc_mei_rx_cb ) ;
2015-05-07 15:54:04 +03:00
if ( r ) {
pr_err ( " Event cb registration failed %d \n " , r ) ;
goto err ;
}
phy - > powered = 1 ;
return 0 ;
err :
phy - > powered = 0 ;
2015-09-10 10:18:05 +03:00
mei_cldev_disable ( phy - > cldev ) ;
2015-05-07 15:54:04 +03:00
return r ;
}
static void nfc_mei_phy_disable ( void * phy_id )
{
struct nfc_mei_phy * phy = phy_id ;
2015-09-10 10:18:05 +03:00
mei_cldev_disable ( phy - > cldev ) ;
2015-05-07 15:54:04 +03:00
phy - > powered = 0 ;
}
2013-04-15 13:19:20 +04:00
2021-07-25 00:47:36 +03:00
const struct nfc_phy_ops mei_phy_ops = {
2013-04-15 13:19:20 +04:00
. write = nfc_mei_phy_write ,
. enable = nfc_mei_phy_enable ,
. disable = nfc_mei_phy_disable ,
} ;
EXPORT_SYMBOL_GPL ( mei_phy_ops ) ;
2015-09-10 10:18:04 +03:00
struct nfc_mei_phy * nfc_mei_phy_alloc ( struct mei_cl_device * cldev )
2013-04-15 13:19:20 +04:00
{
struct nfc_mei_phy * phy ;
phy = kzalloc ( sizeof ( struct nfc_mei_phy ) , GFP_KERNEL ) ;
if ( ! phy )
return NULL ;
2015-09-10 10:18:04 +03:00
phy - > cldev = cldev ;
2015-05-07 15:54:04 +03:00
init_waitqueue_head ( & phy - > send_wq ) ;
2015-09-10 10:18:05 +03:00
mei_cldev_set_drvdata ( cldev , phy ) ;
2013-04-15 13:19:20 +04:00
return phy ;
}
EXPORT_SYMBOL_GPL ( nfc_mei_phy_alloc ) ;
void nfc_mei_phy_free ( struct nfc_mei_phy * phy )
{
2015-09-10 10:18:05 +03:00
mei_cldev_disable ( phy - > cldev ) ;
2013-04-15 13:19:20 +04:00
kfree ( phy ) ;
}
EXPORT_SYMBOL_GPL ( nfc_mei_phy_free ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " mei bus NFC device interface " ) ;