2012-12-25 21:06:07 +04:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
* Copyright ( c ) 2003 - 2012 , 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/pci.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/mei.h>
# include "mei_dev.h"
2013-01-09 01:07:12 +04:00
# include "hbm.h"
2013-01-09 01:07:17 +04:00
# include "hw-me.h"
2014-02-17 17:13:23 +04:00
# include "client.h"
2012-12-25 21:06:07 +04:00
2014-02-17 17:13:20 +04:00
static const char * mei_cl_conn_status_str ( enum mei_cl_connect_status status )
{
# define MEI_CL_CS(status) case MEI_CL_CONN_##status: return #status
switch ( status ) {
MEI_CL_CS ( SUCCESS ) ;
MEI_CL_CS ( NOT_FOUND ) ;
MEI_CL_CS ( ALREADY_STARTED ) ;
MEI_CL_CS ( OUT_OF_RESOURCES ) ;
MEI_CL_CS ( MESSAGE_SMALL ) ;
default : return " unknown " ;
}
# undef MEI_CL_CCS
}
/**
* mei_cl_conn_status_to_errno - convert client connect response
* status to error code
*
* @ status : client connect response status
*
* returns corresponding error code
*/
static int mei_cl_conn_status_to_errno ( enum mei_cl_connect_status status )
{
switch ( status ) {
case MEI_CL_CONN_SUCCESS : return 0 ;
case MEI_CL_CONN_NOT_FOUND : return - ENOTTY ;
case MEI_CL_CONN_ALREADY_STARTED : return - EBUSY ;
case MEI_CL_CONN_OUT_OF_RESOURCES : return - EBUSY ;
case MEI_CL_CONN_MESSAGE_SMALL : return - EINVAL ;
default : return - EINVAL ;
}
}
2013-01-09 01:07:19 +04:00
/**
* mei_hbm_me_cl_allocate - allocates storage for me clients
*
* @ dev : the device structure
*
2014-01-08 22:19:21 +04:00
* returns 0 on success - ENOMEM on allocation failure
2013-01-09 01:07:19 +04:00
*/
2014-01-08 22:19:21 +04:00
static int mei_hbm_me_cl_allocate ( struct mei_device * dev )
2013-01-09 01:07:19 +04:00
{
struct mei_me_client * clients ;
int b ;
2013-09-02 14:29:45 +04:00
dev - > me_clients_num = 0 ;
dev - > me_client_presentation_num = 0 ;
dev - > me_client_index = 0 ;
2013-01-09 01:07:19 +04:00
/* count how many ME clients we have */
for_each_set_bit ( b , dev - > me_clients_map , MEI_CLIENTS_MAX )
dev - > me_clients_num + + ;
2013-09-02 14:29:45 +04:00
if ( dev - > me_clients_num = = 0 )
2014-01-08 22:19:21 +04:00
return 0 ;
2013-01-09 01:07:19 +04:00
kfree ( dev - > me_clients ) ;
dev - > me_clients = NULL ;
2013-09-02 04:11:03 +04:00
dev_dbg ( & dev - > pdev - > dev , " memory allocation for ME clients size=%ld. \n " ,
2013-01-09 01:07:19 +04:00
dev - > me_clients_num * sizeof ( struct mei_me_client ) ) ;
/* allocate storage for ME clients representation */
clients = kcalloc ( dev - > me_clients_num ,
sizeof ( struct mei_me_client ) , GFP_KERNEL ) ;
if ( ! clients ) {
dev_err ( & dev - > pdev - > dev , " memory allocation for ME clients failed. \n " ) ;
2014-01-08 22:19:21 +04:00
return - ENOMEM ;
2013-01-09 01:07:19 +04:00
}
dev - > me_clients = clients ;
2014-01-08 22:19:21 +04:00
return 0 ;
2013-01-09 01:07:19 +04:00
}
2012-12-25 21:06:09 +04:00
/**
* mei_hbm_cl_hdr - construct client hbm header
2013-04-04 20:05:05 +04:00
*
2012-12-25 21:06:09 +04:00
* @ cl : - client
* @ hbm_cmd : host bus message command
* @ buf : buffer for cl header
* @ len : buffer length
*/
static inline
void mei_hbm_cl_hdr ( struct mei_cl * cl , u8 hbm_cmd , void * buf , size_t len )
{
struct mei_hbm_cl_cmd * cmd = buf ;
memset ( cmd , 0 , len ) ;
cmd - > hbm_cmd = hbm_cmd ;
cmd - > host_addr = cl - > host_client_id ;
cmd - > me_addr = cl - > me_client_id ;
}
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_cl_addr_equal - tells if they have the same address
2012-12-25 21:06:09 +04:00
*
2014-01-09 00:31:46 +04:00
* @ cl : - client
* @ buf : buffer with cl header
2012-12-25 21:06:09 +04:00
*
2014-01-09 00:31:46 +04:00
* returns true if addresses are the same
2012-12-25 21:06:09 +04:00
*/
static inline
bool mei_hbm_cl_addr_equal ( struct mei_cl * cl , void * buf )
{
struct mei_hbm_cl_cmd * cmd = buf ;
return cl - > host_client_id = = cmd - > host_addr & &
cl - > me_client_id = = cmd - > me_addr ;
}
2014-01-08 22:19:22 +04:00
/**
* mei_hbm_idle - set hbm to idle state
*
* @ dev : the device structure
*/
void mei_hbm_idle ( struct mei_device * dev )
{
dev - > init_clients_timer = 0 ;
dev - > hbm_state = MEI_HBM_IDLE ;
}
2013-04-19 00:03:48 +04:00
int mei_hbm_start_wait ( struct mei_device * dev )
{
int ret ;
if ( dev - > hbm_state > MEI_HBM_START )
return 0 ;
mutex_unlock ( & dev - > device_lock ) ;
ret = wait_event_interruptible_timeout ( dev - > wait_recvd_msg ,
dev - > hbm_state = = MEI_HBM_IDLE | |
2014-01-08 22:19:21 +04:00
dev - > hbm_state > = MEI_HBM_STARTED ,
2014-01-15 01:10:10 +04:00
mei_secs_to_jiffies ( MEI_HBM_TIMEOUT ) ) ;
2013-04-19 00:03:48 +04:00
mutex_lock ( & dev - > device_lock ) ;
if ( ret < = 0 & & ( dev - > hbm_state < = MEI_HBM_START ) ) {
dev - > hbm_state = MEI_HBM_IDLE ;
2013-05-21 18:13:12 +04:00
dev_err ( & dev - > pdev - > dev , " waiting for mei start failed \n " ) ;
2013-04-19 00:03:48 +04:00
return - ETIMEDOUT ;
}
return 0 ;
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_start_req - sends start request message .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2014-01-08 22:19:21 +04:00
*
* returns 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2013-04-19 00:03:48 +04:00
int mei_hbm_start_req ( struct mei_device * dev )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
struct hbm_host_version_request * start_req ;
const size_t len = sizeof ( struct hbm_host_version_request ) ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
2012-12-25 21:06:07 +04:00
/* host start message */
2012-12-25 21:06:10 +04:00
start_req = ( struct hbm_host_version_request * ) dev - > wr_msg . data ;
2012-12-25 21:06:07 +04:00
memset ( start_req , 0 , len ) ;
start_req - > hbm_cmd = HOST_START_REQ_CMD ;
start_req - > host_version . major_version = HBM_MAJOR_VERSION ;
start_req - > host_version . minor_version = HBM_MINOR_VERSION ;
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_IDLE ;
2014-01-08 22:19:21 +04:00
ret = mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " version message write failed: ret = %d \n " ,
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
2014-01-08 22:19:21 +04:00
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_START ;
2012-12-25 21:06:07 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2013-04-19 00:03:48 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2013-04-19 00:03:48 +04:00
/*
2012-12-25 21:06:11 +04:00
* mei_hbm_enum_clients_req - sends enumeration client request message .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
*
2014-01-08 22:19:21 +04:00
* returns 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2014-01-08 22:19:21 +04:00
static int mei_hbm_enum_clients_req ( struct mei_device * dev )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
struct hbm_host_enum_request * enum_req ;
const size_t len = sizeof ( struct hbm_host_enum_request ) ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
/* enumerate clients */
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:10 +04:00
enum_req = ( struct hbm_host_enum_request * ) dev - > wr_msg . data ;
memset ( enum_req , 0 , len ) ;
2012-12-25 21:06:07 +04:00
enum_req - > hbm_cmd = HOST_ENUM_REQ_CMD ;
2014-01-08 22:19:21 +04:00
ret = mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " enumeration request write failed: ret = %d. \n " ,
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_ENUM_CLIENTS ;
2012-12-25 21:06:07 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2014-01-08 22:19:21 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2012-12-25 21:06:11 +04:00
/**
2013-04-04 20:05:05 +04:00
* mei_hbm_prop_req - request property for a single client
2012-12-25 21:06:11 +04:00
*
* @ dev : the device structure
*
2014-01-08 22:19:21 +04:00
* returns 0 on success and < 0 on failure
2012-12-25 21:06:11 +04:00
*/
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:11 +04:00
static int mei_hbm_prop_req ( struct mei_device * dev )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
struct hbm_props_request * prop_req ;
const size_t len = sizeof ( struct hbm_props_request ) ;
unsigned long next_client_index ;
2013-09-02 14:29:45 +04:00
unsigned long client_num ;
2014-01-08 22:19:21 +04:00
int ret ;
2012-12-25 21:06:07 +04:00
client_num = dev - > me_client_presentation_num ;
next_client_index = find_next_bit ( dev - > me_clients_map , MEI_CLIENTS_MAX ,
dev - > me_client_index ) ;
/* We got all client properties */
if ( next_client_index = = MEI_CLIENTS_MAX ) {
2013-04-19 00:03:48 +04:00
dev - > hbm_state = MEI_HBM_STARTED ;
2012-12-25 21:06:07 +04:00
schedule_work ( & dev - > init_work ) ;
return 0 ;
}
dev - > me_clients [ client_num ] . client_id = next_client_index ;
dev - > me_clients [ client_num ] . mei_flow_ctrl_creds = 0 ;
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
prop_req = ( struct hbm_props_request * ) dev - > wr_msg . data ;
2012-12-25 21:06:07 +04:00
memset ( prop_req , 0 , sizeof ( struct hbm_props_request ) ) ;
prop_req - > hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD ;
prop_req - > address = next_client_index ;
2014-01-08 22:19:21 +04:00
ret = mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
if ( ret ) {
dev_err ( & dev - > pdev - > dev , " properties request write failed: ret = %d \n " ,
ret ) ;
return ret ;
2012-12-25 21:06:07 +04:00
}
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
dev - > me_client_index = next_client_index ;
return 0 ;
}
2012-12-25 21:06:10 +04:00
/**
2014-02-12 23:41:52 +04:00
* mei_hbm_stop_req - send stop request message
2012-12-25 21:06:10 +04:00
*
* @ dev - mei device
2014-02-12 23:41:52 +04:00
* @ cl : client info
*
* This function returns - EIO on write failure
2012-12-25 21:06:10 +04:00
*/
2014-02-12 23:41:52 +04:00
static int mei_hbm_stop_req ( struct mei_device * dev )
2012-12-25 21:06:10 +04:00
{
2014-02-12 23:41:52 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:10 +04:00
struct hbm_host_stop_request * req =
2014-02-12 23:41:52 +04:00
( struct hbm_host_stop_request * ) dev - > wr_msg . data ;
2012-12-25 21:06:10 +04:00
const size_t len = sizeof ( struct hbm_host_stop_request ) ;
mei_hbm_hdr ( mei_hdr , len ) ;
memset ( req , 0 , len ) ;
req - > hbm_cmd = HOST_STOP_REQ_CMD ;
req - > reason = DRIVER_STOP_REQUEST ;
2014-02-12 23:41:52 +04:00
return mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
2012-12-25 21:06:10 +04:00
}
2012-12-25 21:06:07 +04:00
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_cl_flow_control_req - sends flow control request .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : client info
2012-12-25 21:06:07 +04:00
*
* This function returns - EIO on write failure
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_flow_control_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_flow_control ) ;
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , MEI_FLOW_CONTROL_CMD , dev - > wr_msg . data , len ) ;
2012-12-25 21:06:07 +04:00
dev_dbg ( & dev - > pdev - > dev , " sending flow control host client = %d, ME client = %d \n " ,
cl - > host_client_id , cl - > me_client_id ) ;
2012-12-25 21:06:10 +04:00
return mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
2012-12-25 21:06:07 +04:00
}
2012-12-25 21:06:12 +04:00
/**
2013-04-04 20:05:05 +04:00
* mei_hbm_add_single_flow_creds - adds single buffer credentials .
2012-12-25 21:06:12 +04:00
*
2013-04-04 20:05:05 +04:00
* @ dev : the device structure
2012-12-25 21:06:12 +04:00
* @ flow : flow control .
2014-02-17 17:13:23 +04:00
*
* return 0 on success , < 0 otherwise
2012-12-25 21:06:12 +04:00
*/
2014-02-17 17:13:23 +04:00
static int mei_hbm_add_single_flow_creds ( struct mei_device * dev ,
2012-12-25 21:06:12 +04:00
struct hbm_flow_control * flow )
{
2014-02-17 17:13:23 +04:00
struct mei_me_client * me_cl ;
int id ;
id = mei_me_cl_by_id ( dev , flow - > me_addr ) ;
if ( id < 0 ) {
dev_err ( & dev - > pdev - > dev , " no such me client %d \n " ,
flow - > me_addr ) ;
return id ;
}
me_cl = & dev - > me_clients [ id ] ;
if ( me_cl - > props . single_recv_buf ) {
me_cl - > mei_flow_ctrl_creds + + ;
dev_dbg ( & dev - > pdev - > dev , " recv flow ctrl msg ME %d (single). \n " ,
flow - > me_addr ) ;
dev_dbg ( & dev - > pdev - > dev , " flow control credentials =%d. \n " ,
me_cl - > mei_flow_ctrl_creds ) ;
} else {
BUG ( ) ; /* error in flow control */
2012-12-25 21:06:12 +04:00
}
2014-02-17 17:13:23 +04:00
return 0 ;
2012-12-25 21:06:12 +04:00
}
/**
* mei_hbm_cl_flow_control_res - flow control response from me
*
* @ dev : the device structure
* @ flow_control : flow control response bus message
*/
static void mei_hbm_cl_flow_control_res ( struct mei_device * dev ,
struct hbm_flow_control * flow_control )
{
struct mei_cl * cl = NULL ;
struct mei_cl * next = NULL ;
if ( ! flow_control - > host_addr ) {
/* single receive buffer */
mei_hbm_add_single_flow_creds ( dev , flow_control ) ;
return ;
}
/* normal connection */
list_for_each_entry_safe ( cl , next , & dev - > file_list , link ) {
if ( mei_hbm_cl_addr_equal ( cl , flow_control ) ) {
cl - > mei_flow_ctrl_creds + + ;
dev_dbg ( & dev - > pdev - > dev , " flow ctrl msg for host %d ME %d. \n " ,
flow_control - > host_addr , flow_control - > me_addr ) ;
dev_dbg ( & dev - > pdev - > dev , " flow control credentials = %d. \n " ,
cl - > mei_flow_ctrl_creds ) ;
break ;
}
}
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_cl_disconnect_req - sends disconnect message to fw .
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : a client to disconnect from
2012-12-25 21:06:07 +04:00
*
* This function returns - EIO on write failure
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_disconnect_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_client_connect_request ) ;
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , CLIENT_DISCONNECT_REQ_CMD , dev - > wr_msg . data , len ) ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:10 +04:00
return mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
2012-12-25 21:06:07 +04:00
}
2014-02-12 23:41:52 +04:00
/**
* mei_hbm_cl_disconnect_rsp - sends disconnect respose to the FW
*
* @ dev : the device structure
* @ cl : a client to disconnect from
*
* This function returns - EIO on write failure
*/
int mei_hbm_cl_disconnect_rsp ( struct mei_device * dev , struct mei_cl * cl )
{
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
const size_t len = sizeof ( struct hbm_client_connect_response ) ;
mei_hbm_hdr ( mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , CLIENT_DISCONNECT_RES_CMD , dev - > wr_msg . data , len ) ;
return mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
}
2012-12-25 21:06:12 +04:00
/**
* mei_hbm_cl_disconnect_res - disconnect response from ME
*
* @ dev : the device structure
* @ rs : disconnect response bus message
*/
static void mei_hbm_cl_disconnect_res ( struct mei_device * dev ,
struct hbm_client_connect_response * rs )
{
struct mei_cl * cl ;
2014-02-17 17:13:21 +04:00
struct mei_cl_cb * cb , * next ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:20 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: disconnect response cl:host=%02d me=%02d status=%d \n " ,
rs - > me_addr , rs - > host_addr , rs - > status ) ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
list_for_each_entry_safe ( cb , next , & dev - > ctrl_rd_list . list , list ) {
cl = cb - > cl ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
/* this should not happen */
if ( WARN_ON ( ! cl ) ) {
list_del ( & cb - > list ) ;
2012-12-25 21:06:12 +04:00
return ;
}
if ( mei_hbm_cl_addr_equal ( cl , rs ) ) {
2014-02-17 17:13:21 +04:00
list_del ( & cb - > list ) ;
2014-02-17 17:13:20 +04:00
if ( rs - > status = = MEI_CL_DISCONN_SUCCESS )
2012-12-25 21:06:12 +04:00
cl - > state = MEI_FILE_DISCONNECTED ;
cl - > status = 0 ;
cl - > timer_count = 0 ;
break ;
}
}
}
2012-12-25 21:06:07 +04:00
/**
2012-12-25 21:06:11 +04:00
* mei_hbm_cl_connect_req - send connection request to specific me client
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure
2012-12-25 21:06:11 +04:00
* @ cl : a client to connect to
2012-12-25 21:06:07 +04:00
*
2012-12-25 21:06:11 +04:00
* returns - EIO on write failure
2012-12-25 21:06:07 +04:00
*/
2012-12-25 21:06:11 +04:00
int mei_hbm_cl_connect_req ( struct mei_device * dev , struct mei_cl * cl )
2012-12-25 21:06:07 +04:00
{
2012-12-25 21:06:10 +04:00
struct mei_msg_hdr * mei_hdr = & dev - > wr_msg . hdr ;
2012-12-25 21:06:07 +04:00
const size_t len = sizeof ( struct hbm_client_connect_request ) ;
2012-12-25 21:06:10 +04:00
mei_hbm_hdr ( mei_hdr , len ) ;
mei_hbm_cl_hdr ( cl , CLIENT_CONNECT_REQ_CMD , dev - > wr_msg . data , len ) ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:10 +04:00
return mei_write_message ( dev , mei_hdr , dev - > wr_msg . data ) ;
2012-12-25 21:06:07 +04:00
}
2012-12-25 21:06:12 +04:00
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_cl_connect_res - connect response from the ME
2012-12-25 21:06:12 +04:00
*
* @ dev : the device structure
* @ rs : connect response bus message
*/
static void mei_hbm_cl_connect_res ( struct mei_device * dev ,
struct hbm_client_connect_response * rs )
{
struct mei_cl * cl ;
2014-02-17 17:13:21 +04:00
struct mei_cl_cb * cb , * next ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:20 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: connect response cl:host=%02d me=%02d status=%s \n " ,
rs - > me_addr , rs - > host_addr ,
mei_cl_conn_status_str ( rs - > status ) ) ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
cl = NULL ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
list_for_each_entry_safe ( cb , next , & dev - > ctrl_rd_list . list , list ) {
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
cl = cb - > cl ;
/* this should not happen */
if ( WARN_ON ( ! cl ) ) {
list_del_init ( & cb - > list ) ;
continue ;
}
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
if ( cb - > fop_type ! = MEI_FOP_CONNECT )
continue ;
2012-12-25 21:06:12 +04:00
2014-02-17 17:13:21 +04:00
if ( mei_hbm_cl_addr_equal ( cl , rs ) ) {
list_del ( & cb - > list ) ;
break ;
2012-12-25 21:06:12 +04:00
}
}
2014-02-17 17:13:21 +04:00
if ( ! cl )
return ;
cl - > timer_count = 0 ;
if ( rs - > status = = MEI_CL_CONN_SUCCESS )
cl - > state = MEI_FILE_CONNECTED ;
else
cl - > state = MEI_FILE_DISCONNECTED ;
cl - > status = mei_cl_conn_status_to_errno ( rs - > status ) ;
2012-12-25 21:06:12 +04:00
}
2012-12-25 21:06:07 +04:00
/**
2014-01-09 00:31:46 +04:00
* mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
* host sends disconnect response
2012-12-25 21:06:07 +04:00
*
* @ dev : the device structure .
2012-12-25 21:06:11 +04:00
* @ disconnect_req : disconnect request bus message from the me
2014-02-12 23:41:52 +04:00
*
* returns - ENOMEM on allocation failure
2012-12-25 21:06:07 +04:00
*/
2014-02-12 23:41:52 +04:00
static int mei_hbm_fw_disconnect_req ( struct mei_device * dev ,
2012-12-25 21:06:07 +04:00
struct hbm_client_connect_request * disconnect_req )
{
2012-12-25 21:06:09 +04:00
struct mei_cl * cl , * next ;
2014-02-12 23:41:52 +04:00
struct mei_cl_cb * cb ;
2012-12-25 21:06:07 +04:00
2012-12-25 21:06:09 +04:00
list_for_each_entry_safe ( cl , next , & dev - > file_list , link ) {
if ( mei_hbm_cl_addr_equal ( cl , disconnect_req ) ) {
2012-12-25 21:06:07 +04:00
dev_dbg ( & dev - > pdev - > dev , " disconnect request host client %d ME client %d. \n " ,
disconnect_req - > host_addr ,
disconnect_req - > me_addr ) ;
2012-12-25 21:06:09 +04:00
cl - > state = MEI_FILE_DISCONNECTED ;
cl - > timer_count = 0 ;
2012-12-25 21:06:07 +04:00
2014-02-12 23:41:52 +04:00
cb = mei_io_cb_init ( cl , NULL ) ;
if ( ! cb )
return - ENOMEM ;
cb - > fop_type = MEI_FOP_DISCONNECT_RSP ;
cl_dbg ( dev , cl , " add disconnect response as first \n " ) ;
list_add ( & cb - > list , & dev - > ctrl_wr_list . list ) ;
2012-12-25 21:06:07 +04:00
break ;
}
}
2014-02-12 23:41:52 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}
2013-06-16 10:16:31 +04:00
/**
* mei_hbm_version_is_supported - checks whether the driver can
* support the hbm version of the device
*
* @ dev : the device structure
* returns true if driver can support hbm version of the device
*/
bool mei_hbm_version_is_supported ( struct mei_device * dev )
{
return ( dev - > version . major_version < HBM_MAJOR_VERSION ) | |
( dev - > version . major_version = = HBM_MAJOR_VERSION & &
dev - > version . minor_version < = HBM_MINOR_VERSION ) ;
}
2012-12-25 21:06:07 +04:00
/**
* mei_hbm_dispatch - bottom half read routine after ISR to
* handle the read bus message cmd processing .
*
* @ dev : the device structure
* @ mei_hdr : header of bus message
2014-01-08 22:19:21 +04:00
*
* returns 0 on success and < 0 on failure
2012-12-25 21:06:07 +04:00
*/
2014-01-08 22:19:21 +04:00
int mei_hbm_dispatch ( struct mei_device * dev , struct mei_msg_hdr * hdr )
2012-12-25 21:06:07 +04:00
{
struct mei_bus_message * mei_msg ;
struct mei_me_client * me_client ;
struct hbm_host_version_response * version_res ;
struct hbm_client_connect_response * connect_res ;
struct hbm_client_connect_response * disconnect_res ;
struct hbm_client_connect_request * disconnect_req ;
struct hbm_flow_control * flow_control ;
struct hbm_props_response * props_res ;
struct hbm_host_enum_response * enum_res ;
/* read the message to our buffer */
BUG_ON ( hdr - > length > = sizeof ( dev - > rd_msg_buf ) ) ;
mei_read_slots ( dev , dev - > rd_msg_buf , hdr - > length ) ;
mei_msg = ( struct mei_bus_message * ) dev - > rd_msg_buf ;
2014-01-08 22:19:22 +04:00
/* ignore spurious message and prevent reset nesting
* hbm is put to idle during system reset
*/
if ( dev - > hbm_state = = MEI_HBM_IDLE ) {
dev_dbg ( & dev - > pdev - > dev , " hbm: state is idle ignore spurious messages \n " ) ;
return 0 ;
}
2012-12-25 21:06:07 +04:00
switch ( mei_msg - > hbm_cmd ) {
case HOST_START_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: start: response message received. \n " ) ;
dev - > init_clients_timer = 0 ;
2012-12-25 21:06:07 +04:00
version_res = ( struct hbm_host_version_response * ) mei_msg ;
2013-06-16 10:16:31 +04:00
dev_dbg ( & dev - > pdev - > dev , " HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d \n " ,
HBM_MAJOR_VERSION , HBM_MINOR_VERSION ,
version_res - > me_max_version . major_version ,
version_res - > me_max_version . minor_version ) ;
if ( version_res - > host_version_supported ) {
dev - > version . major_version = HBM_MAJOR_VERSION ;
dev - > version . minor_version = HBM_MINOR_VERSION ;
} else {
dev - > version . major_version =
version_res - > me_max_version . major_version ;
dev - > version . minor_version =
version_res - > me_max_version . minor_version ;
}
if ( ! mei_hbm_version_is_supported ( dev ) ) {
2014-01-08 22:19:21 +04:00
dev_warn ( & dev - > pdev - > dev , " hbm: start: version mismatch - stopping the driver. \n " ) ;
2012-12-25 21:06:07 +04:00
2014-01-08 22:19:21 +04:00
dev - > hbm_state = MEI_HBM_STOPPED ;
2014-02-12 23:41:52 +04:00
if ( mei_hbm_stop_req ( dev ) ) {
2014-01-08 22:19:21 +04:00
dev_err ( & dev - > pdev - > dev , " hbm: start: failed to send stop request \n " ) ;
return - EIO ;
}
break ;
}
2013-04-19 00:03:48 +04:00
2014-01-08 22:19:21 +04:00
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
dev - > hbm_state ! = MEI_HBM_START ) {
dev_err ( & dev - > pdev - > dev , " hbm: start: state mismatch, [%d, %d] \n " ,
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
2012-12-25 21:06:10 +04:00
}
2012-12-25 21:06:07 +04:00
2014-01-08 22:19:21 +04:00
dev - > hbm_state = MEI_HBM_STARTED ;
if ( mei_hbm_enum_clients_req ( dev ) ) {
dev_err ( & dev - > pdev - > dev , " hbm: start: failed to send enumeration request \n " ) ;
return - EIO ;
2012-12-25 21:06:07 +04:00
}
2013-04-19 00:03:48 +04:00
wake_up_interruptible ( & dev - > wait_recvd_msg ) ;
2012-12-25 21:06:07 +04:00
break ;
case CLIENT_CONNECT_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: client connect response: message received. \n " ) ;
2012-12-25 21:06:07 +04:00
connect_res = ( struct hbm_client_connect_response * ) mei_msg ;
2012-12-25 21:06:12 +04:00
mei_hbm_cl_connect_res ( dev , connect_res ) ;
2012-12-25 21:06:07 +04:00
wake_up ( & dev - > wait_recvd_msg ) ;
break ;
case CLIENT_DISCONNECT_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: client disconnect response: message received. \n " ) ;
2012-12-25 21:06:07 +04:00
disconnect_res = ( struct hbm_client_connect_response * ) mei_msg ;
2012-12-25 21:06:12 +04:00
mei_hbm_cl_disconnect_res ( dev , disconnect_res ) ;
2012-12-25 21:06:07 +04:00
wake_up ( & dev - > wait_recvd_msg ) ;
break ;
case MEI_FLOW_CONTROL_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: client flow control response: message received. \n " ) ;
2012-12-25 21:06:07 +04:00
flow_control = ( struct hbm_flow_control * ) mei_msg ;
2012-12-25 21:06:12 +04:00
mei_hbm_cl_flow_control_res ( dev , flow_control ) ;
2012-12-25 21:06:07 +04:00
break ;
case HOST_CLIENT_PROPERTIES_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: properties response: message received. \n " ) ;
dev - > init_clients_timer = 0 ;
if ( dev - > me_clients = = NULL ) {
dev_err ( & dev - > pdev - > dev , " hbm: properties response: mei_clients not allocated \n " ) ;
return - EPROTO ;
}
2012-12-25 21:06:07 +04:00
props_res = ( struct hbm_props_response * ) mei_msg ;
me_client = & dev - > me_clients [ dev - > me_client_presentation_num ] ;
2014-01-08 22:19:21 +04:00
if ( props_res - > status ) {
dev_err ( & dev - > pdev - > dev , " hbm: properties response: wrong status = %d \n " ,
props_res - > status ) ;
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
if ( me_client - > client_id ! = props_res - > address ) {
2014-01-08 22:19:21 +04:00
dev_err ( & dev - > pdev - > dev , " hbm: properties response: address mismatch %d ?= %d \n " ,
me_client - > client_id , props_res - > address ) ;
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
2013-04-19 00:03:48 +04:00
dev - > hbm_state ! = MEI_HBM_CLIENT_PROPERTIES ) {
2014-01-08 22:19:21 +04:00
dev_err ( & dev - > pdev - > dev , " hbm: properties response: state mismatch, [%d, %d] \n " ,
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
me_client - > props = props_res - > client_properties ;
dev - > me_client_index + + ;
dev - > me_client_presentation_num + + ;
2012-12-25 21:06:11 +04:00
/* request property for the next client */
2014-01-08 22:19:21 +04:00
if ( mei_hbm_prop_req ( dev ) )
return - EIO ;
2012-12-25 21:06:07 +04:00
break ;
case HOST_ENUM_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: enumeration response: message received \n " ) ;
dev - > init_clients_timer = 0 ;
2012-12-25 21:06:07 +04:00
enum_res = ( struct hbm_host_enum_response * ) mei_msg ;
2013-09-02 04:11:01 +04:00
BUILD_BUG_ON ( sizeof ( dev - > me_clients_map )
< sizeof ( enum_res - > valid_addresses ) ) ;
memcpy ( dev - > me_clients_map , enum_res - > valid_addresses ,
sizeof ( enum_res - > valid_addresses ) ) ;
2014-01-08 22:19:21 +04:00
if ( dev - > dev_state ! = MEI_DEV_INIT_CLIENTS | |
dev - > hbm_state ! = MEI_HBM_ENUM_CLIENTS ) {
dev_err ( & dev - > pdev - > dev , " hbm: enumeration response: state mismatch, [%d, %d] \n " ,
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
2012-12-25 21:06:07 +04:00
}
2014-01-08 22:19:21 +04:00
if ( mei_hbm_me_cl_allocate ( dev ) ) {
dev_err ( & dev - > pdev - > dev , " hbm: enumeration response: cannot allocate clients array \n " ) ;
return - ENOMEM ;
}
dev - > hbm_state = MEI_HBM_CLIENT_PROPERTIES ;
/* first property request */
if ( mei_hbm_prop_req ( dev ) )
return - EIO ;
2012-12-25 21:06:07 +04:00
break ;
case HOST_STOP_RES_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: stop response: message received \n " ) ;
dev - > init_clients_timer = 0 ;
if ( dev - > hbm_state ! = MEI_HBM_STOPPED ) {
dev_err ( & dev - > pdev - > dev , " hbm: stop response: state mismatch, [%d, %d] \n " ,
dev - > dev_state , dev - > hbm_state ) ;
return - EPROTO ;
}
2013-04-19 00:03:48 +04:00
2014-01-12 02:36:09 +04:00
dev - > dev_state = MEI_DEV_POWER_DOWN ;
2014-01-08 22:19:21 +04:00
dev_info ( & dev - > pdev - > dev , " hbm: stop response: resetting. \n " ) ;
/* force the reset */
return - EPROTO ;
2012-12-25 21:06:07 +04:00
break ;
case CLIENT_DISCONNECT_REQ_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: disconnect request: message received \n " ) ;
2012-12-25 21:06:07 +04:00
disconnect_req = ( struct hbm_client_connect_request * ) mei_msg ;
2012-12-25 21:06:11 +04:00
mei_hbm_fw_disconnect_req ( dev , disconnect_req ) ;
2012-12-25 21:06:07 +04:00
break ;
case ME_STOP_REQ_CMD :
2014-01-08 22:19:21 +04:00
dev_dbg ( & dev - > pdev - > dev , " hbm: stop request: message received \n " ) ;
dev - > hbm_state = MEI_HBM_STOPPED ;
2014-02-12 23:41:52 +04:00
if ( mei_hbm_stop_req ( dev ) ) {
dev_err ( & dev - > pdev - > dev , " hbm: start: failed to send stop request \n " ) ;
return - EIO ;
}
2012-12-25 21:06:07 +04:00
break ;
default :
BUG ( ) ;
break ;
}
2014-01-08 22:19:21 +04:00
return 0 ;
2012-12-25 21:06:07 +04:00
}