2011-05-15 14:43:44 +04:00
/*
*
* Intel Management Engine Interface ( Intel MEI ) Linux driver
2012-02-09 21:25:53 +04:00
* Copyright ( c ) 2003 - 2012 , Intel Corporation .
2011-05-15 14:43:44 +04:00
*
* 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/delay.h>
# include "mei_dev.h"
# include "hw.h"
# include "interface.h"
2012-05-09 17:38:59 +04:00
# include <linux/mei.h>
2011-05-15 14:43:44 +04:00
2012-08-07 01:03:56 +04:00
const char * mei_dev_state_str ( int state )
{
# define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
switch ( state ) {
MEI_DEV_STATE ( INITIALIZING ) ;
MEI_DEV_STATE ( INIT_CLIENTS ) ;
MEI_DEV_STATE ( ENABLED ) ;
MEI_DEV_STATE ( RESETING ) ;
MEI_DEV_STATE ( DISABLED ) ;
MEI_DEV_STATE ( RECOVERING_FROM_RESET ) ;
MEI_DEV_STATE ( POWER_DOWN ) ;
MEI_DEV_STATE ( POWER_UP ) ;
default :
return " unkown " ;
}
# undef MEI_DEV_STATE
}
2012-11-18 17:13:14 +04:00
2011-05-15 14:43:44 +04:00
/**
2011-06-06 11:44:34 +04:00
* mei_io_list_flush - removes list entry belonging to cl .
2011-05-15 14:43:44 +04:00
*
* @ list : An instance of our list structure
* @ cl : private data of the file object
*/
2012-10-15 14:06:48 +04:00
void mei_io_list_flush ( struct mei_cl_cb * list , struct mei_cl * cl )
2011-05-15 14:43:44 +04:00
{
2011-12-04 18:16:27 +04:00
struct mei_cl_cb * pos ;
struct mei_cl_cb * next ;
2011-05-15 14:43:44 +04:00
2012-10-15 14:06:48 +04:00
list_for_each_entry_safe ( pos , next , & list - > list , list ) {
2012-11-11 19:37:59 +04:00
if ( pos - > cl ) {
if ( mei_cl_cmp_id ( cl , pos - > cl ) )
2012-10-15 14:06:48 +04:00
list_del ( & pos - > list ) ;
2011-05-15 14:43:44 +04:00
}
}
}
2011-06-06 11:44:34 +04:00
/**
* mei_cl_flush_queues - flushes queue lists belonging to cl .
*
* @ dev : the device structure
* @ cl : private data of the file object
*/
int mei_cl_flush_queues ( struct mei_cl * cl )
{
if ( ! cl | | ! cl - > dev )
return - EINVAL ;
dev_dbg ( & cl - > dev - > pdev - > dev , " remove list entry belonging to cl \n " ) ;
mei_io_list_flush ( & cl - > dev - > read_list , cl ) ;
mei_io_list_flush ( & cl - > dev - > write_list , cl ) ;
mei_io_list_flush ( & cl - > dev - > write_waiting_list , cl ) ;
mei_io_list_flush ( & cl - > dev - > ctrl_wr_list , cl ) ;
mei_io_list_flush ( & cl - > dev - > ctrl_rd_list , cl ) ;
2012-11-11 19:37:58 +04:00
mei_io_list_flush ( & cl - > dev - > amthif_cmd_list , cl ) ;
mei_io_list_flush ( & cl - > dev - > amthif_rd_complete_list , cl ) ;
2011-06-06 11:44:34 +04:00
return 0 ;
}
2011-05-15 14:43:44 +04:00
/**
* init_mei_device - allocates and initializes the mei device structure
*
* @ pdev : The pci device structure
*
* returns The mei_device_device pointer on success , NULL on failure .
*/
2011-05-25 18:28:21 +04:00
struct mei_device * mei_device_init ( struct pci_dev * pdev )
2011-05-15 14:43:44 +04:00
{
struct mei_device * dev ;
dev = kzalloc ( sizeof ( struct mei_device ) , GFP_KERNEL ) ;
if ( ! dev )
return NULL ;
/* setup our list array */
INIT_LIST_HEAD ( & dev - > file_list ) ;
INIT_LIST_HEAD ( & dev - > wd_cl . link ) ;
INIT_LIST_HEAD ( & dev - > iamthif_cl . link ) ;
mutex_init ( & dev - > device_lock ) ;
init_waitqueue_head ( & dev - > wait_recvd_msg ) ;
init_waitqueue_head ( & dev - > wait_stop_wd ) ;
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_INITIALIZING ;
2011-05-15 14:43:44 +04:00
dev - > iamthif_state = MEI_IAMTHIF_IDLE ;
2011-06-06 11:44:34 +04:00
mei_io_list_init ( & dev - > read_list ) ;
mei_io_list_init ( & dev - > write_list ) ;
mei_io_list_init ( & dev - > write_waiting_list ) ;
mei_io_list_init ( & dev - > ctrl_wr_list ) ;
mei_io_list_init ( & dev - > ctrl_rd_list ) ;
2012-11-11 19:37:58 +04:00
mei_io_list_init ( & dev - > amthif_cmd_list ) ;
mei_io_list_init ( & dev - > amthif_rd_complete_list ) ;
2011-05-15 14:43:44 +04:00
dev - > pdev = pdev ;
return dev ;
}
/**
* mei_hw_init - initializes host and fw to start work .
*
* @ dev : the device structure
*
* returns 0 on success , < 0 on failure .
*/
int mei_hw_init ( struct mei_device * dev )
{
int err = 0 ;
int ret ;
mutex_lock ( & dev - > device_lock ) ;
dev - > host_hw_state = mei_hcsr_read ( dev ) ;
dev - > me_hw_state = mei_mecsr_read ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " host_hw_state = 0x%08x, mestate = 0x%08x. \n " ,
dev - > host_hw_state , dev - > me_hw_state ) ;
/* acknowledge interrupt and stop interupts */
if ( ( dev - > host_hw_state & H_IS ) = = H_IS )
mei_reg_write ( dev , H_CSR , dev - > host_hw_state ) ;
2012-06-26 00:46:27 +04:00
/* Doesn't change in runtime */
dev - > hbuf_depth = ( dev - > host_hw_state & H_CBD ) > > 24 ;
2011-05-25 18:28:22 +04:00
dev - > recvd_msg = false ;
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " reset in start the mei device. \n " ) ;
mei_reset ( dev , 1 ) ;
dev_dbg ( & dev - > pdev - > dev , " host_hw_state = 0x%08x, me_hw_state = 0x%08x. \n " ,
dev - > host_hw_state , dev - > me_hw_state ) ;
/* wait for ME to turn on ME_RDY */
if ( ! dev - > recvd_msg ) {
mutex_unlock ( & dev - > device_lock ) ;
err = wait_event_interruptible_timeout ( dev - > wait_recvd_msg ,
2012-11-01 23:17:14 +04:00
dev - > recvd_msg ,
mei_secs_to_jiffies ( MEI_INTEROP_TIMEOUT ) ) ;
2011-05-15 14:43:44 +04:00
mutex_lock ( & dev - > device_lock ) ;
}
2011-06-13 17:39:31 +04:00
if ( err < = 0 & & ! dev - > recvd_msg ) {
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_DISABLED ;
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev ,
" wait_event_interruptible_timeout failed "
" on wait for ME to turn on ME_RDY. \n " ) ;
ret = - ENODEV ;
goto out ;
}
if ( ! ( ( ( dev - > host_hw_state & H_RDY ) = = H_RDY ) & &
( ( dev - > me_hw_state & ME_RDY_HRA ) = = ME_RDY_HRA ) ) ) {
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_DISABLED ;
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev ,
" host_hw_state = 0x%08x, me_hw_state = 0x%08x. \n " ,
dev - > host_hw_state , dev - > me_hw_state ) ;
2011-06-09 11:18:23 +04:00
if ( ! ( dev - > host_hw_state & H_RDY ) )
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " host turn off H_RDY. \n " ) ;
2011-06-09 11:18:23 +04:00
if ( ! ( dev - > me_hw_state & ME_RDY_HRA ) )
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " ME turn off ME_RDY. \n " ) ;
2012-03-19 19:58:42 +04:00
dev_err ( & dev - > pdev - > dev , " link layer initialization failed. \n " ) ;
2011-05-15 14:43:44 +04:00
ret = - ENODEV ;
goto out ;
}
if ( dev - > version . major_version ! = HBM_MAJOR_VERSION | |
dev - > version . minor_version ! = HBM_MINOR_VERSION ) {
dev_dbg ( & dev - > pdev - > dev , " MEI start failed. \n " ) ;
ret = - ENODEV ;
goto out ;
}
2011-05-25 18:28:22 +04:00
dev - > recvd_msg = false ;
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " host_hw_state = 0x%08x, me_hw_state = 0x%08x. \n " ,
dev - > host_hw_state , dev - > me_hw_state ) ;
dev_dbg ( & dev - > pdev - > dev , " ME turn on ME_RDY and host turn on H_RDY. \n " ) ;
dev_dbg ( & dev - > pdev - > dev , " link layer has been established. \n " ) ;
dev_dbg ( & dev - > pdev - > dev , " MEI start success. \n " ) ;
ret = 0 ;
out :
mutex_unlock ( & dev - > device_lock ) ;
return ret ;
}
/**
* mei_hw_reset - resets fw via mei csr register .
*
* @ dev : the device structure
* @ interrupts_enabled : if interrupt should be enabled after reset .
*/
static void mei_hw_reset ( struct mei_device * dev , int interrupts_enabled )
{
dev - > host_hw_state | = ( H_RST | H_IG ) ;
if ( interrupts_enabled )
mei_enable_interrupts ( dev ) ;
else
mei_disable_interrupts ( dev ) ;
}
/**
* mei_reset - resets host and fw .
*
* @ dev : the device structure
* @ interrupts_enabled : if interrupt should be enabled after reset .
*/
void mei_reset ( struct mei_device * dev , int interrupts_enabled )
{
struct mei_cl * cl_pos = NULL ;
struct mei_cl * cl_next = NULL ;
struct mei_cl_cb * cb_pos = NULL ;
struct mei_cl_cb * cb_next = NULL ;
bool unexpected ;
2012-08-07 01:03:56 +04:00
if ( dev - > dev_state = = MEI_DEV_RECOVERING_FROM_RESET ) {
2011-05-25 18:28:22 +04:00
dev - > need_reset = true ;
2011-05-15 14:43:44 +04:00
return ;
}
2012-08-07 01:03:56 +04:00
unexpected = ( dev - > dev_state ! = MEI_DEV_INITIALIZING & &
dev - > dev_state ! = MEI_DEV_DISABLED & &
dev - > dev_state ! = MEI_DEV_POWER_DOWN & &
dev - > dev_state ! = MEI_DEV_POWER_UP ) ;
2011-05-15 14:43:44 +04:00
dev - > host_hw_state = mei_hcsr_read ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " before reset host_hw_state = 0x%08x. \n " ,
dev - > host_hw_state ) ;
mei_hw_reset ( dev , interrupts_enabled ) ;
dev - > host_hw_state & = ~ H_RST ;
dev - > host_hw_state | = H_IG ;
mei_hcsr_set ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " currently saved host_hw_state = 0x%08x. \n " ,
dev - > host_hw_state ) ;
2011-05-25 18:28:22 +04:00
dev - > need_reset = false ;
2011-05-15 14:43:44 +04:00
2012-08-07 01:03:56 +04:00
if ( dev - > dev_state ! = MEI_DEV_INITIALIZING ) {
if ( dev - > dev_state ! = MEI_DEV_DISABLED & &
dev - > dev_state ! = MEI_DEV_POWER_DOWN )
dev - > dev_state = MEI_DEV_RESETING ;
2011-05-15 14:43:44 +04:00
list_for_each_entry_safe ( cl_pos ,
cl_next , & dev - > file_list , link ) {
cl_pos - > state = MEI_FILE_DISCONNECTED ;
cl_pos - > mei_flow_ctrl_creds = 0 ;
cl_pos - > read_cb = NULL ;
cl_pos - > timer_count = 0 ;
}
/* remove entry if already in list */
2012-11-11 19:38:03 +04:00
dev_dbg ( & dev - > pdev - > dev , " remove iamthif and wd from the file list. \n " ) ;
mei_me_cl_unlink ( dev , & dev - > wd_cl ) ;
2011-05-15 14:43:44 +04:00
2012-11-11 19:38:03 +04:00
mei_me_cl_unlink ( dev , & dev - > iamthif_cl ) ;
2011-05-15 14:43:44 +04:00
2012-11-01 23:17:15 +04:00
mei_amthif_reset_params ( dev ) ;
2012-11-18 17:13:15 +04:00
memset ( & dev - > wr_ext_msg , 0 , sizeof ( dev - > wr_ext_msg ) ) ;
2011-05-15 14:43:44 +04:00
}
2011-06-06 11:44:33 +04:00
dev - > me_clients_num = 0 ;
2011-05-15 14:43:44 +04:00
dev - > rd_msg_hdr = 0 ;
2011-05-25 18:28:22 +04:00
dev - > wd_pending = false ;
2011-05-15 14:43:44 +04:00
/* update the state of the registers after reset */
dev - > host_hw_state = mei_hcsr_read ( dev ) ;
dev - > me_hw_state = mei_mecsr_read ( dev ) ;
dev_dbg ( & dev - > pdev - > dev , " after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x. \n " ,
dev - > host_hw_state , dev - > me_hw_state ) ;
if ( unexpected )
2012-08-07 01:03:56 +04:00
dev_warn ( & dev - > pdev - > dev , " unexpected reset: dev_state = %s \n " ,
mei_dev_state_str ( dev - > dev_state ) ) ;
2011-05-15 14:43:44 +04:00
/* Wake up all readings so they can be interrupted */
list_for_each_entry_safe ( cl_pos , cl_next , & dev - > file_list , link ) {
if ( waitqueue_active ( & cl_pos - > rx_wait ) ) {
dev_dbg ( & dev - > pdev - > dev , " Waking up client! \n " ) ;
wake_up_interruptible ( & cl_pos - > rx_wait ) ;
}
}
/* remove all waiting requests */
2012-10-15 14:06:48 +04:00
list_for_each_entry_safe ( cb_pos , cb_next , & dev - > write_list . list , list ) {
list_del ( & cb_pos - > list ) ;
2012-10-09 18:50:20 +04:00
mei_io_cb_free ( cb_pos ) ;
2011-05-15 14:43:44 +04:00
}
}
/**
* host_start_message - mei host sends start message .
*
* @ dev : the device structure
*
* returns none .
*/
2011-05-25 18:28:21 +04:00
void mei_host_start_message ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
struct mei_msg_hdr * mei_hdr ;
2012-11-18 17:13:14 +04:00
struct hbm_host_version_request * start_req ;
const size_t len = sizeof ( struct hbm_host_version_request ) ;
mei_hdr = mei_hbm_hdr ( & dev - > wr_msg_buf [ 0 ] , len ) ;
2011-05-15 14:43:44 +04:00
/* host start message */
2012-11-18 17:13:14 +04:00
start_req = ( struct hbm_host_version_request * ) & dev - > wr_msg_buf [ 1 ] ;
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 ;
2011-05-25 18:28:22 +04:00
dev - > recvd_msg = false ;
2012-11-18 17:13:14 +04:00
if ( mei_write_message ( dev , mei_hdr , ( unsigned char * ) start_req , len ) ) {
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " write send version message to FW fail. \n " ) ;
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_RESETING ;
2011-05-15 14:43:44 +04:00
mei_reset ( dev , 1 ) ;
}
dev - > init_clients_state = MEI_START_MESSAGE ;
2012-11-01 23:17:14 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2011-05-15 14:43:44 +04:00
return ;
}
/**
* host_enum_clients_message - host sends enumeration client request message .
*
* @ dev : the device structure
*
* returns none .
*/
2011-05-25 18:28:21 +04:00
void mei_host_enum_clients_message ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
struct mei_msg_hdr * mei_hdr ;
2012-11-18 17:13:14 +04:00
struct hbm_host_enum_request * enum_req ;
const size_t len = sizeof ( struct hbm_host_enum_request ) ;
2011-05-15 14:43:44 +04:00
/* enumerate clients */
2012-11-18 17:13:14 +04:00
mei_hdr = mei_hbm_hdr ( & dev - > wr_msg_buf [ 0 ] , len ) ;
enum_req = ( struct hbm_host_enum_request * ) & dev - > wr_msg_buf [ 1 ] ;
memset ( enum_req , 0 , sizeof ( struct hbm_host_enum_request ) ) ;
enum_req - > hbm_cmd = HOST_ENUM_REQ_CMD ;
if ( mei_write_message ( dev , mei_hdr , ( unsigned char * ) enum_req , len ) ) {
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_RESETING ;
2011-05-15 14:43:44 +04:00
dev_dbg ( & dev - > pdev - > dev , " write send enumeration request message to FW fail. \n " ) ;
mei_reset ( dev , 1 ) ;
}
dev - > init_clients_state = MEI_ENUM_CLIENTS_MESSAGE ;
2012-11-01 23:17:14 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
2012-03-14 16:39:42 +04:00
return ;
2011-05-15 14:43:44 +04:00
}
/**
* allocate_me_clients_storage - allocates storage for me clients
*
* @ dev : the device structure
*
* returns none .
*/
2011-05-25 18:28:21 +04:00
void mei_allocate_me_clients_storage ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
struct mei_me_client * clients ;
int b ;
/* count how many ME clients we have */
for_each_set_bit ( b , dev - > me_clients_map , MEI_CLIENTS_MAX )
2011-06-06 11:44:33 +04:00
dev - > me_clients_num + + ;
2011-05-15 14:43:44 +04:00
2011-06-06 11:44:33 +04:00
if ( dev - > me_clients_num < = 0 )
2011-05-15 14:43:44 +04:00
return ;
if ( dev - > me_clients ! = NULL ) {
kfree ( dev - > me_clients ) ;
dev - > me_clients = NULL ;
}
dev_dbg ( & dev - > pdev - > dev , " memory allocation for ME clients size=%zd. \n " ,
2011-06-06 11:44:33 +04:00
dev - > me_clients_num * sizeof ( struct mei_me_client ) ) ;
2011-05-15 14:43:44 +04:00
/* allocate storage for ME clients representation */
2011-06-06 11:44:33 +04:00
clients = kcalloc ( dev - > me_clients_num ,
2011-05-15 14:43:44 +04:00
sizeof ( struct mei_me_client ) , GFP_KERNEL ) ;
if ( ! clients ) {
dev_dbg ( & dev - > pdev - > dev , " memory allocation for ME clients failed. \n " ) ;
2012-08-07 01:03:56 +04:00
dev - > dev_state = MEI_DEV_RESETING ;
2011-05-15 14:43:44 +04:00
mei_reset ( dev , 1 ) ;
return ;
}
dev - > me_clients = clients ;
return ;
}
2012-11-18 17:13:20 +04:00
void mei_host_client_init ( struct work_struct * work )
{
struct mei_device * dev = container_of ( work ,
struct mei_device , init_work ) ;
struct mei_client_properties * client_props ;
int i ;
mutex_lock ( & dev - > device_lock ) ;
bitmap_zero ( dev - > host_clients_map , MEI_CLIENTS_MAX ) ;
dev - > open_handle_count = 0 ;
/*
* Reserving the first three client IDs
* 0 : Reserved for MEI Bus Message communications
* 1 : Reserved for Watchdog
* 2 : Reserved for AMTHI
*/
bitmap_set ( dev - > host_clients_map , 0 , 3 ) ;
for ( i = 0 ; i < dev - > me_clients_num ; i + + ) {
client_props = & dev - > me_clients [ i ] . props ;
if ( ! uuid_le_cmp ( client_props - > protocol_name , mei_amthi_guid ) )
mei_amthif_host_init ( dev ) ;
else if ( ! uuid_le_cmp ( client_props - > protocol_name , mei_wd_guid ) )
mei_wd_host_init ( dev ) ;
}
dev - > dev_state = MEI_DEV_ENABLED ;
mutex_unlock ( & dev - > device_lock ) ;
}
int mei_host_client_enumerate ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
2012-11-18 17:13:14 +04:00
struct mei_msg_hdr * mei_hdr ;
struct hbm_props_request * prop_req ;
const size_t len = sizeof ( struct hbm_props_request ) ;
2012-11-18 17:13:20 +04:00
unsigned long next_client_index ;
u8 client_num ;
2012-11-18 17:13:14 +04:00
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
client_num = dev - > me_client_presentation_num ;
2012-11-18 17:13:14 +04:00
2012-11-18 17:13:20 +04:00
next_client_index = find_next_bit ( dev - > me_clients_map , MEI_CLIENTS_MAX ,
dev - > me_client_index ) ;
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
/* We got all client properties */
if ( next_client_index = = MEI_CLIENTS_MAX ) {
schedule_work ( & dev - > init_work ) ;
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
return 0 ;
}
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
dev - > me_clients [ client_num ] . client_id = next_client_index ;
dev - > me_clients [ client_num ] . mei_flow_ctrl_creds = 0 ;
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +04:00
mei_hdr = mei_hbm_hdr ( & dev - > wr_msg_buf [ 0 ] , len ) ;
prop_req = ( struct hbm_props_request * ) & dev - > wr_msg_buf [ 1 ] ;
2011-05-15 14:43:44 +04:00
2012-11-18 17:13:20 +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 ;
if ( mei_write_message ( dev , mei_hdr , ( unsigned char * ) prop_req ,
mei_hdr - > length ) ) {
dev - > dev_state = MEI_DEV_RESETING ;
dev_err ( & dev - > pdev - > dev , " Properties request command failed \n " ) ;
mei_reset ( dev , 1 ) ;
return - EIO ;
2011-05-15 14:43:44 +04:00
}
2012-11-18 17:13:20 +04:00
dev - > init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT ;
dev - > me_client_index = next_client_index ;
2011-09-21 17:45:30 +04:00
return 0 ;
2011-05-15 14:43:44 +04:00
}
/**
* mei_init_file_private - initializes private file structure .
*
* @ priv : private file structure to be initialized
* @ file : the file structure
*/
2011-05-25 18:28:21 +04:00
void mei_cl_init ( struct mei_cl * priv , struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
memset ( priv , 0 , sizeof ( struct mei_cl ) ) ;
init_waitqueue_head ( & priv - > wait ) ;
init_waitqueue_head ( & priv - > rx_wait ) ;
init_waitqueue_head ( & priv - > tx_wait ) ;
INIT_LIST_HEAD ( & priv - > link ) ;
priv - > reading_state = MEI_IDLE ;
priv - > writing_state = MEI_IDLE ;
priv - > dev = dev ;
}
2012-07-23 15:05:39 +04:00
int mei_me_cl_by_uuid ( const struct mei_device * dev , const uuid_le * cuuid )
2011-05-15 14:43:44 +04:00
{
2012-07-23 15:05:39 +04:00
int i , res = - ENOENT ;
2011-05-15 14:43:44 +04:00
2011-06-06 11:44:33 +04:00
for ( i = 0 ; i < dev - > me_clients_num ; + + i )
2012-07-23 15:05:39 +04:00
if ( uuid_le_cmp ( * cuuid ,
2011-05-15 14:43:44 +04:00
dev - > me_clients [ i ] . props . protocol_name ) = = 0 ) {
res = i ;
break ;
}
return res ;
}
/**
2012-11-11 19:38:03 +04:00
* mei_me_cl_link - create link between host and me clinet and add
* me_cl to the list
*
2011-05-15 14:43:44 +04:00
* @ dev : the device structure
2012-11-11 19:38:03 +04:00
* @ cl : link between me and host client assocated with opened file descriptor
* @ cuuid : uuid of ME client
* @ client_id : id of the host client
2011-05-15 14:43:44 +04:00
*
2012-11-11 19:38:03 +04:00
* returns ME client index if ME client
* - EINVAL on incorrect values
* - ENONET if client not found
2011-05-15 14:43:44 +04:00
*/
2012-11-11 19:38:03 +04:00
int mei_me_cl_link ( struct mei_device * dev , struct mei_cl * cl ,
const uuid_le * cuuid , u8 host_cl_id )
2011-05-15 14:43:44 +04:00
{
int i ;
2012-07-23 15:05:39 +04:00
if ( ! dev | | ! cl | | ! cuuid )
return - EINVAL ;
2011-05-15 14:43:44 +04:00
/* check for valid client id */
2012-07-23 15:05:39 +04:00
i = mei_me_cl_by_uuid ( dev , cuuid ) ;
2011-05-15 14:43:44 +04:00
if ( i > = 0 ) {
2012-07-23 15:05:39 +04:00
cl - > me_client_id = dev - > me_clients [ i ] . client_id ;
cl - > state = MEI_FILE_CONNECTING ;
cl - > host_client_id = host_cl_id ;
2011-05-15 14:43:44 +04:00
2012-07-23 15:05:39 +04:00
list_add_tail ( & cl - > link , & dev - > file_list ) ;
2011-05-15 14:43:44 +04:00
return ( u8 ) i ;
}
2012-07-23 15:05:39 +04:00
return - ENOENT ;
2011-05-15 14:43:44 +04:00
}
2012-11-11 19:38:03 +04:00
/**
* mei_me_cl_unlink - remove me_cl from the list
*
* @ dev : the device structure
* @ host_client_id : host client id to be removed
*/
void mei_me_cl_unlink ( struct mei_device * dev , struct mei_cl * cl )
{
struct mei_cl * pos , * next ;
list_for_each_entry_safe ( pos , next , & dev - > file_list , link ) {
if ( cl - > host_client_id = = pos - > host_client_id ) {
dev_dbg ( & dev - > pdev - > dev , " remove host client = %d, ME client = %d \n " ,
pos - > host_client_id , pos - > me_client_id ) ;
list_del_init ( & pos - > link ) ;
break ;
}
}
}
2011-05-15 14:43:44 +04:00
/**
* mei_alloc_file_private - allocates a private file structure and sets it up .
* @ file : the file structure
*
* returns The allocated file or NULL on failure
*/
2011-05-25 18:28:21 +04:00
struct mei_cl * mei_cl_allocate ( struct mei_device * dev )
2011-05-15 14:43:44 +04:00
{
2011-05-25 18:28:21 +04:00
struct mei_cl * cl ;
2011-05-15 14:43:44 +04:00
2011-05-25 18:28:21 +04:00
cl = kmalloc ( sizeof ( struct mei_cl ) , GFP_KERNEL ) ;
if ( ! cl )
2011-05-15 14:43:44 +04:00
return NULL ;
2011-05-25 18:28:21 +04:00
mei_cl_init ( cl , dev ) ;
2011-05-15 14:43:44 +04:00
2011-05-25 18:28:21 +04:00
return cl ;
2011-05-15 14:43:44 +04:00
}
/**
* mei_disconnect_host_client - sends disconnect message to fw from host client .
*
* @ dev : the device structure
* @ cl : private data of the file object
*
* Locking : called under " dev->device_lock " lock
*
* returns 0 on success , < 0 on failure .
*/
int mei_disconnect_host_client ( struct mei_device * dev , struct mei_cl * cl )
{
struct mei_cl_cb * cb ;
2012-11-01 23:17:14 +04:00
int rets , err ;
2011-05-15 14:43:44 +04:00
if ( ! dev | | ! cl )
return - ENODEV ;
if ( cl - > state ! = MEI_FILE_DISCONNECTING )
return 0 ;
2012-10-11 18:35:08 +04:00
cb = mei_io_cb_init ( cl , NULL ) ;
2011-05-15 14:43:44 +04:00
if ( ! cb )
return - ENOMEM ;
2012-11-11 19:38:00 +04:00
cb - > fop_type = MEI_FOP_CLOSE ;
2011-05-15 14:43:44 +04:00
if ( dev - > mei_host_buffer_is_empty ) {
2011-05-25 18:28:22 +04:00
dev - > mei_host_buffer_is_empty = false ;
2011-05-15 14:43:44 +04:00
if ( mei_disconnect ( dev , cl ) ) {
rets = - ENODEV ;
dev_dbg ( & dev - > pdev - > dev , " failed to call mei_disconnect. \n " ) ;
goto free ;
}
2012-03-14 16:39:42 +04:00
mdelay ( 10 ) ; /* Wait for hardware disconnection ready */
2012-10-15 14:06:48 +04:00
list_add_tail ( & cb - > list , & dev - > ctrl_rd_list . list ) ;
2011-05-15 14:43:44 +04:00
} else {
dev_dbg ( & dev - > pdev - > dev , " add disconnect cb to control write list \n " ) ;
2012-10-15 14:06:48 +04:00
list_add_tail ( & cb - > list , & dev - > ctrl_wr_list . list ) ;
2011-05-15 14:43:44 +04:00
}
mutex_unlock ( & dev - > device_lock ) ;
err = wait_event_timeout ( dev - > wait_recvd_msg ,
2012-11-01 23:17:14 +04:00
MEI_FILE_DISCONNECTED = = cl - > state ,
mei_secs_to_jiffies ( MEI_CL_CONNECT_TIMEOUT ) ) ;
2011-05-15 14:43:44 +04:00
mutex_lock ( & dev - > device_lock ) ;
if ( MEI_FILE_DISCONNECTED = = cl - > state ) {
rets = 0 ;
dev_dbg ( & dev - > pdev - > dev , " successfully disconnected from FW client. \n " ) ;
} else {
rets = - ENODEV ;
if ( MEI_FILE_DISCONNECTED ! = cl - > state )
dev_dbg ( & dev - > pdev - > dev , " wrong status client disconnect. \n " ) ;
if ( err )
dev_dbg ( & dev - > pdev - > dev ,
" wait failed disconnect err=%08x \n " ,
err ) ;
dev_dbg ( & dev - > pdev - > dev , " failed to disconnect from FW client. \n " ) ;
}
2011-06-06 11:44:34 +04:00
mei_io_list_flush ( & dev - > ctrl_rd_list , cl ) ;
mei_io_list_flush ( & dev - > ctrl_wr_list , cl ) ;
2011-05-15 14:43:44 +04:00
free :
2012-10-09 18:50:20 +04:00
mei_io_cb_free ( cb ) ;
2011-05-15 14:43:44 +04:00
return rets ;
}