2013-04-11 05:03:29 +04:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
* Copyright ( c ) 2003 - 2013 , Intel Corporation .
*
* 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 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/kernel.h>
2013-04-11 05:03:31 +04:00
# include <linux/sched.h>
2013-04-11 05:03:29 +04:00
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/device.h>
2014-09-29 17:31:46 +04:00
# include <linux/slab.h>
2015-07-23 15:08:43 +03:00
# include <linux/uuid.h>
2014-09-29 17:31:46 +04:00
2013-04-11 05:03:29 +04:00
# include <linux/mei_cl_bus.h>
# include "mei_dev.h"
# include "client.h"
2015-07-23 15:08:44 +03:00
# define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \
0x48 , 0xa4 , 0xef , 0xab , 0xba , 0x8a , 0x12 , 0x06 )
2015-07-23 15:08:47 +03:00
static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO ;
# define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \
0x94 , 0xd4 , 0x50 , 0x26 , 0x67 , 0x23 , 0x77 , 0x5c )
2016-01-08 01:49:24 +03:00
# define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \
0x89 , 0x9D , 0xA9 , 0x15 , 0x14 , 0xCB , 0x32 , 0xAB )
2015-07-23 15:08:43 +03:00
# define MEI_UUID_ANY NULL_UUID_LE
2015-07-23 15:08:45 +03:00
/**
* number_of_connections - determine whether an client be on the bus
* according number of connections
* We support only clients :
* 1. with single connection
* 2. and fixed clients ( max_number_of_connections = = 0 )
*
* @ cldev : me clients device
*/
static void number_of_connections ( struct mei_cl_device * cldev )
{
2016-01-07 15:46:34 +03:00
dev_dbg ( & cldev - > dev , " running hook %s \n " , __func__ ) ;
2015-07-23 15:08:45 +03:00
if ( cldev - > me_cl - > props . max_number_of_connections > 1 )
cldev - > do_match = 0 ;
}
2015-07-23 15:08:44 +03:00
/**
* blacklist - blacklist a client from the bus
*
* @ cldev : me clients device
*/
static void blacklist ( struct mei_cl_device * cldev )
{
2016-01-07 15:46:34 +03:00
dev_dbg ( & cldev - > dev , " running hook %s \n " , __func__ ) ;
2015-07-23 15:08:44 +03:00
cldev - > do_match = 0 ;
}
2016-01-08 01:49:24 +03:00
/**
* mei_wd - wd client on the bus , change protocol version
* as the API has changed .
*
* @ cldev : me clients device
*/
# if IS_ENABLED(CONFIG_INTEL_MEI_ME)
# include <linux/pci.h>
# include "hw-me-regs.h"
static void mei_wd ( struct mei_cl_device * cldev )
{
struct pci_dev * pdev = to_pci_dev ( cldev - > dev . parent ) ;
dev_dbg ( & cldev - > dev , " running hook %s \n " , __func__ ) ;
if ( pdev - > device = = MEI_DEV_ID_WPT_LP | |
pdev - > device = = MEI_DEV_ID_SPT | |
pdev - > device = = MEI_DEV_ID_SPT_H )
cldev - > me_cl - > props . protocol_version = 0x2 ;
cldev - > do_match = 1 ;
}
# else
static inline void mei_wd ( struct mei_cl_device * cldev ) { }
# endif /* CONFIG_INTEL_MEI_ME */
2013-04-11 05:03:29 +04:00
struct mei_nfc_cmd {
u8 command ;
u8 status ;
u16 req_id ;
u32 reserved ;
u16 data_size ;
u8 sub_command ;
u8 data [ ] ;
} __packed ;
struct mei_nfc_reply {
u8 command ;
u8 status ;
u16 req_id ;
u32 reserved ;
u16 data_size ;
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 ;
# define MEI_NFC_CMD_MAINTENANCE 0x00
# define MEI_NFC_SUBCMD_IF_VERSION 0x01
2013-04-11 05:03:30 +04:00
/* Vendors */
# define MEI_NFC_VENDOR_INSIDE 0x00
# define MEI_NFC_VENDOR_NXP 0x01
/* Radio types */
# define MEI_NFC_VENDOR_INSIDE_UREAD 0x00
# define MEI_NFC_VENDOR_NXP_PN544 0x01
2015-07-23 15:08:46 +03:00
/**
* mei_nfc_if_version - get NFC interface version
*
* @ cl : host client ( nfc info )
* @ ver : NFC interface version to be filled in
*
* Return : 0 on success ; < 0 otherwise
*/
static int mei_nfc_if_version ( struct mei_cl * cl ,
struct mei_nfc_if_version * ver )
2013-04-11 05:03:29 +04:00
{
2015-07-23 15:08:33 +03:00
struct mei_device * bus ;
2015-07-23 15:08:46 +03:00
struct mei_nfc_cmd cmd = {
. command = MEI_NFC_CMD_MAINTENANCE ,
. data_size = 1 ,
. sub_command = MEI_NFC_SUBCMD_IF_VERSION ,
} ;
2013-04-11 05:03:29 +04:00
struct mei_nfc_reply * reply = NULL ;
size_t if_version_length ;
int bytes_recv , ret ;
2015-07-23 15:08:33 +03:00
bus = cl - > dev ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:46 +03:00
WARN_ON ( mutex_is_locked ( & bus - > device_lock ) ) ;
2013-04-11 05:03:29 +04:00
2015-05-07 15:54:04 +03:00
ret = __mei_cl_send ( cl , ( u8 * ) & cmd , sizeof ( struct mei_nfc_cmd ) , 1 ) ;
2013-04-11 05:03:29 +04:00
if ( ret < 0 ) {
2015-07-23 15:08:33 +03:00
dev_err ( bus - > dev , " Could not send IF version cmd \n " ) ;
2013-04-11 05:03:29 +04:00
return ret ;
}
/* to be sure on the stack we alloc memory */
if_version_length = sizeof ( struct mei_nfc_reply ) +
sizeof ( struct mei_nfc_if_version ) ;
reply = kzalloc ( if_version_length , GFP_KERNEL ) ;
if ( ! reply )
return - ENOMEM ;
2015-07-23 15:08:46 +03:00
ret = 0 ;
2013-04-11 05:03:29 +04:00
bytes_recv = __mei_cl_recv ( cl , ( u8 * ) reply , if_version_length ) ;
if ( bytes_recv < 0 | | bytes_recv < sizeof ( struct mei_nfc_reply ) ) {
2015-07-23 15:08:33 +03:00
dev_err ( bus - > dev , " Could not read IF version \n " ) ;
2013-04-11 05:03:29 +04:00
ret = - EIO ;
goto err ;
}
2015-07-23 15:08:46 +03:00
memcpy ( ver , reply - > data , sizeof ( struct mei_nfc_if_version ) ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:46 +03:00
dev_info ( bus - > dev , " NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x \n " ,
ver - > fw_ivn , ver - > vendor_id , ver - > radio_type ) ;
2013-04-11 05:03:29 +04:00
err :
kfree ( reply ) ;
return ret ;
}
2015-07-23 15:08:46 +03:00
/**
* mei_nfc_radio_name - derive nfc radio name from the interface version
*
* @ ver : NFC radio version
*
* Return : radio name string
*/
static const char * mei_nfc_radio_name ( struct mei_nfc_if_version * ver )
{
if ( ver - > vendor_id = = MEI_NFC_VENDOR_INSIDE ) {
if ( ver - > radio_type = = MEI_NFC_VENDOR_INSIDE_UREAD )
return " microread " ;
}
if ( ver - > vendor_id = = MEI_NFC_VENDOR_NXP ) {
if ( ver - > radio_type = = MEI_NFC_VENDOR_NXP_PN544 )
return " pn544 " ;
}
return NULL ;
}
2015-07-23 15:08:47 +03:00
/**
* mei_nfc - The nfc fixup function . The function retrieves nfc radio
* name and set is as device attribute so we can load
* the proper device driver for it
*
* @ cldev : me client device ( nfc )
*/
static void mei_nfc ( struct mei_cl_device * cldev )
2013-04-11 05:03:29 +04:00
{
2015-07-23 15:08:33 +03:00
struct mei_device * bus ;
2015-07-23 15:08:47 +03:00
struct mei_cl * cl ;
struct mei_me_client * me_cl = NULL ;
struct mei_nfc_if_version ver ;
const char * radio_name = NULL ;
int ret ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
bus = cldev - > bus ;
2013-04-11 05:03:29 +04:00
2016-01-07 15:46:34 +03:00
dev_dbg ( & cldev - > dev , " running hook %s \n " , __func__ ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:33 +03:00
mutex_lock ( & bus - > device_lock ) ;
2015-07-23 15:08:47 +03:00
/* we need to connect to INFO GUID */
2016-02-08 00:35:40 +03:00
cl = mei_cl_alloc_linked ( bus ) ;
2015-07-23 15:08:47 +03:00
if ( IS_ERR ( cl ) ) {
ret = PTR_ERR ( cl ) ;
cl = NULL ;
dev_err ( bus - > dev , " nfc hook alloc failed %d \n " , ret ) ;
goto out ;
2013-04-11 05:03:29 +04:00
}
2015-07-23 15:08:47 +03:00
me_cl = mei_me_cl_by_uuid ( bus , & mei_nfc_info_guid ) ;
if ( ! me_cl ) {
ret = - ENOTTY ;
dev_err ( bus - > dev , " Cannot find nfc info %d \n " , ret ) ;
goto out ;
2013-04-11 05:03:29 +04:00
}
2015-07-23 15:08:47 +03:00
ret = mei_cl_connect ( cl , me_cl , NULL ) ;
if ( ret < 0 ) {
dev_err ( & cldev - > dev , " Can't connect to the NFC INFO ME ret = %d \n " ,
ret ) ;
goto out ;
2013-04-11 05:03:29 +04:00
}
2015-07-23 15:08:33 +03:00
mutex_unlock ( & bus - > device_lock ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
ret = mei_nfc_if_version ( cl , & ver ) ;
if ( ret )
goto disconnect ;
2015-07-23 15:08:46 +03:00
2015-07-23 15:08:47 +03:00
radio_name = mei_nfc_radio_name ( & ver ) ;
2013-04-11 05:03:30 +04:00
2015-07-23 15:08:47 +03:00
if ( ! radio_name ) {
ret = - ENOENT ;
dev_err ( & cldev - > dev , " Can't get the NFC interface version ret = %d \n " ,
ret ) ;
goto disconnect ;
2013-04-11 05:03:30 +04:00
}
2015-07-23 15:08:47 +03:00
dev_dbg ( bus - > dev , " nfc radio %s \n " , radio_name ) ;
strlcpy ( cldev - > name , radio_name , sizeof ( cldev - > name ) ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
disconnect :
2015-07-23 15:08:33 +03:00
mutex_lock ( & bus - > device_lock ) ;
2015-07-23 15:08:47 +03:00
if ( mei_cl_disconnect ( cl ) < 0 )
dev_err ( bus - > dev , " Can't disconnect the NFC INFO ME \n " ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
mei_cl_flush_queues ( cl , NULL ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
out :
mei_cl_unlink ( cl ) ;
mutex_unlock ( & bus - > device_lock ) ;
mei_me_cl_put ( me_cl ) ;
kfree ( cl ) ;
2013-04-11 05:03:29 +04:00
2015-07-23 15:08:47 +03:00
if ( ret )
cldev - > do_match = 0 ;
2015-07-08 00:22:03 +03:00
2015-07-23 15:08:47 +03:00
dev_dbg ( bus - > dev , " end of fixup match = %d \n " , cldev - > do_match ) ;
2014-02-17 17:13:19 +04:00
}
2013-11-11 15:26:06 +04:00
2015-07-23 15:08:43 +03:00
# define MEI_FIXUP(_uuid, _hook) { _uuid, _hook }
static struct mei_fixup {
const uuid_le uuid ;
void ( * hook ) ( struct mei_cl_device * cldev ) ;
2015-07-23 15:08:44 +03:00
} mei_fixups [ ] = {
2015-07-23 15:08:45 +03:00
MEI_FIXUP ( MEI_UUID_ANY , number_of_connections ) ,
2015-07-23 15:08:44 +03:00
MEI_FIXUP ( MEI_UUID_NFC_INFO , blacklist ) ,
2015-07-23 15:08:47 +03:00
MEI_FIXUP ( MEI_UUID_NFC_HCI , mei_nfc ) ,
2016-01-08 01:49:24 +03:00
MEI_FIXUP ( MEI_UUID_WD , mei_wd ) ,
2015-07-23 15:08:44 +03:00
} ;
2015-07-23 15:08:43 +03:00
/**
2015-09-10 10:18:06 +03:00
* mei_cldev_fixup - run fixup handlers
2015-07-23 15:08:43 +03:00
*
* @ cldev : me client device
*/
2015-09-10 10:18:06 +03:00
void mei_cl_bus_dev_fixup ( struct mei_cl_device * cldev )
2015-07-23 15:08:43 +03:00
{
struct mei_fixup * f ;
const uuid_le * uuid = mei_me_cl_uuid ( cldev - > me_cl ) ;
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( mei_fixups ) ; i + + ) {
f = & mei_fixups [ i ] ;
if ( uuid_le_cmp ( f - > uuid , MEI_UUID_ANY ) = = 0 | |
uuid_le_cmp ( f - > uuid , * uuid ) = = 0 )
f - > hook ( cldev ) ;
}
}
2013-04-11 05:03:30 +04:00