2012-11-01 21:17:15 +02: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/kernel.h>
# include <linux/fs.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/fcntl.h>
# include <linux/ioctl.h>
# include <linux/cdev.h>
# include <linux/list.h>
# include <linux/delay.h>
# include <linux/sched.h>
# include <linux/uuid.h>
# include <linux/jiffies.h>
# include <linux/uaccess.h>
2014-09-29 16:31:46 +03:00
# include <linux/slab.h>
2012-11-01 21:17:15 +02:00
2012-12-25 19:06:03 +02:00
# include <linux/mei.h>
2012-11-01 21:17:15 +02:00
# include "mei_dev.h"
2013-01-08 23:07:12 +02:00
# include "hbm.h"
2013-01-08 23:07:14 +02:00
# include "client.h"
2012-11-01 21:17:15 +02:00
2013-01-08 23:07:21 +02:00
const uuid_le mei_amthif_guid = UUID_LE ( 0x12f80028 , 0xb4b7 , 0x4b2d ,
0xac , 0xa8 , 0x46 , 0xe0 ,
0xff , 0x65 , 0x81 , 0x4c ) ;
2012-11-01 21:17:15 +02:00
/**
* mei_amthif_reset_params - initializes mei device iamthif
*
* @ dev : the device structure
*/
void mei_amthif_reset_params ( struct mei_device * dev )
{
/* reset iamthif parameters. */
dev - > iamthif_canceled = false ;
dev - > iamthif_state = MEI_IAMTHIF_IDLE ;
2013-09-02 13:29:47 +03:00
dev - > iamthif_stall_timer = 0 ;
2013-09-16 23:44:47 +03:00
dev - > iamthif_open_count = 0 ;
2012-11-01 21:17:15 +02:00
}
/**
2013-04-05 01:05:05 +09:00
* mei_amthif_host_init - mei initialization amthif client .
2012-11-01 21:17:15 +02:00
*
* @ dev : the device structure
2015-05-04 09:43:54 +03:00
* @ me_cl : me client
2012-11-01 21:17:15 +02:00
*
2014-09-29 16:31:50 +03:00
* Return : 0 on success , < 0 on failure .
2012-11-01 21:17:15 +02:00
*/
2015-05-04 09:43:54 +03:00
int mei_amthif_host_init ( struct mei_device * dev , struct mei_me_client * me_cl )
2012-11-01 21:17:15 +02:00
{
2013-01-08 23:07:22 +02:00
struct mei_cl * cl = & dev - > iamthif_cl ;
2014-08-24 12:08:55 +03:00
int ret ;
2012-11-01 21:17:15 +02:00
2016-07-26 01:06:09 +03:00
mutex_lock ( & dev - > device_lock ) ;
if ( mei_cl_is_connected ( cl ) ) {
ret = 0 ;
goto out ;
}
2016-02-07 23:35:43 +02:00
2013-01-08 23:07:23 +02:00
dev - > iamthif_state = MEI_IAMTHIF_IDLE ;
2013-01-08 23:07:22 +02:00
mei_cl_init ( cl , dev ) ;
2012-11-01 21:17:15 +02:00
2016-02-07 23:35:40 +02:00
ret = mei_cl_link ( cl ) ;
2013-01-08 23:07:22 +02:00
if ( ret < 0 ) {
2015-01-11 00:07:16 +02:00
dev_err ( dev - > dev , " amthif: failed cl_link %d \n " , ret ) ;
2016-07-26 01:06:09 +03:00
goto out ;
2013-01-08 23:07:22 +02:00
}
2015-05-04 09:43:54 +03:00
ret = mei_cl_connect ( cl , me_cl , NULL ) ;
2014-02-17 15:13:21 +02:00
2016-07-26 01:06:09 +03:00
out :
mutex_unlock ( & dev - > device_lock ) ;
2014-02-17 15:13:21 +02:00
return ret ;
2012-11-01 21:17:15 +02:00
}
/**
2015-02-10 10:39:39 +02:00
* mei_amthif_read_start - queue message for sending read credential
2012-11-01 21:17:15 +02:00
*
2015-02-10 10:39:39 +02:00
* @ cl : host client
2016-07-26 01:06:05 +03:00
* @ fp : file pointer of message recipient
2012-11-01 21:17:15 +02:00
*
2014-09-29 16:31:49 +03:00
* Return : 0 on success , < 0 on failure .
2012-11-01 21:17:15 +02:00
*/
2016-07-26 01:06:05 +03:00
static int mei_amthif_read_start ( struct mei_cl * cl , const struct file * fp )
2012-11-01 21:17:15 +02:00
{
2015-02-10 10:39:39 +02:00
struct mei_device * dev = cl - > dev ;
struct mei_cl_cb * cb ;
2012-11-01 21:17:15 +02:00
2016-07-26 01:06:05 +03:00
cb = mei_cl_enqueue_ctrl_wr_cb ( cl , mei_cl_mtu ( cl ) , MEI_FOP_READ , fp ) ;
2016-05-24 16:03:40 -04:00
if ( ! cb )
return - ENOMEM ;
2012-11-01 21:17:15 +02:00
2016-07-26 01:06:03 +03:00
cl - > rx_flow_ctrl_creds + + ;
2012-11-01 21:17:15 +02:00
2015-02-10 10:39:39 +02:00
dev - > iamthif_state = MEI_IAMTHIF_READING ;
2016-06-16 17:58:57 +03:00
cl - > fp = cb - > fp ;
2012-11-01 21:17:15 +02:00
return 0 ;
}
/**
2014-09-29 16:31:50 +03:00
* mei_amthif_run_next_cmd - send next amt command from queue
2012-11-01 21:17:15 +02:00
*
* @ dev : the device structure
2012-11-01 21:17:18 +02:00
*
2014-09-29 16:31:49 +03:00
* Return : 0 on success , < 0 on failure .
2012-11-01 21:17:15 +02:00
*/
2015-02-10 10:39:40 +02:00
int mei_amthif_run_next_cmd ( struct mei_device * dev )
2012-11-01 21:17:15 +02:00
{
2015-02-10 10:39:40 +02:00
struct mei_cl * cl = & dev - > iamthif_cl ;
2014-08-14 17:22:21 +03:00
struct mei_cl_cb * cb ;
2016-05-24 16:03:39 -04:00
int ret ;
2012-11-01 21:17:15 +02:00
dev - > iamthif_canceled = false ;
2014-09-29 16:31:42 +03:00
dev_dbg ( dev - > dev , " complete amthif cmd_list cb. \n " ) ;
2012-11-01 21:17:15 +02:00
2014-10-02 13:39:31 +03:00
cb = list_first_entry_or_null ( & dev - > amthif_cmd_list . list ,
typeof ( * cb ) , list ) ;
2016-05-24 16:03:39 -04:00
if ( ! cb ) {
dev - > iamthif_state = MEI_IAMTHIF_IDLE ;
2016-06-16 17:58:57 +03:00
cl - > fp = NULL ;
2015-02-10 10:39:40 +02:00
return 0 ;
2016-05-24 16:03:39 -04:00
}
2015-02-10 10:39:40 +02:00
2015-02-10 10:39:39 +02:00
list_del_init ( & cb - > list ) ;
2016-05-24 16:03:39 -04:00
dev - > iamthif_state = MEI_IAMTHIF_WRITING ;
2016-06-16 17:58:57 +03:00
cl - > fp = cb - > fp ;
2016-05-24 16:03:39 -04:00
ret = mei_cl_write ( cl , cb , false ) ;
if ( ret < 0 )
return ret ;
if ( cb - > completed )
cb - > status = mei_amthif_read_start ( cl , cb - > fp ) ;
return 0 ;
2012-11-01 21:17:15 +02:00
}
2015-02-10 10:39:40 +02:00
/**
* mei_amthif_write - write amthif data to amthif client
*
* @ cl : host client
* @ cb : mei call back struct
*
* Return : 0 on success , < 0 on failure .
*/
int mei_amthif_write ( struct mei_cl * cl , struct mei_cl_cb * cb )
{
2016-02-07 23:35:27 +02:00
struct mei_device * dev = cl - > dev ;
2015-02-10 10:39:40 +02:00
list_add_tail ( & cb - > list , & dev - > amthif_cmd_list . list ) ;
2016-02-07 23:35:25 +02:00
/*
* The previous request is still in processing , queue this one .
*/
2016-05-24 16:03:35 -04:00
if ( dev - > iamthif_state ! = MEI_IAMTHIF_IDLE )
2016-02-07 23:35:25 +02:00
return 0 ;
2015-02-10 10:39:40 +02:00
return mei_amthif_run_next_cmd ( dev ) ;
}
2012-11-11 17:38:02 +02:00
2015-03-27 00:27:57 +02:00
/**
* mei_amthif_poll - the amthif poll function
*
* @ file : pointer to file structure
* @ wait : pointer to poll_table structure
*
* Return : poll mask
*
* Locking : called under " dev->device_lock " lock
*/
2016-05-24 16:03:35 -04:00
unsigned int mei_amthif_poll ( struct file * file , poll_table * wait )
2012-11-11 17:38:02 +02:00
{
2016-05-24 16:03:35 -04:00
struct mei_cl * cl = file - > private_data ;
struct mei_cl_cb * cb = mei_cl_read_cb ( cl , file ) ;
2012-11-11 17:38:02 +02:00
unsigned int mask = 0 ;
2013-07-25 20:15:53 +03:00
2016-05-24 16:03:35 -04:00
poll_wait ( file , & cl - > rx_wait , wait ) ;
if ( cb )
2015-03-27 00:27:57 +02:00
mask | = POLLIN | POLLRDNORM ;
2013-07-25 20:15:53 +03:00
2012-11-11 17:38:02 +02:00
return mask ;
}
2012-11-01 21:17:15 +02:00
/**
2014-02-19 17:35:48 +02:00
* mei_amthif_irq_write - write iamthif command in irq thread context .
2012-11-01 21:17:15 +02:00
*
* @ cl : private data of the file object .
2014-09-29 16:31:49 +03:00
* @ cb : callback block .
2012-11-01 21:17:15 +02:00
* @ cmpl_list : complete list .
*
2014-09-29 16:31:49 +03:00
* Return : 0 , OK ; otherwise , error .
2012-11-01 21:17:15 +02:00
*/
2014-02-19 17:35:48 +02:00
int mei_amthif_irq_write ( struct mei_cl * cl , struct mei_cl_cb * cb ,
struct mei_cl_cb * cmpl_list )
2012-11-01 21:17:15 +02:00
{
2015-02-10 10:39:40 +02:00
int ret ;
2012-11-18 15:13:17 +02:00
2015-02-10 10:39:40 +02:00
ret = mei_cl_irq_write ( cl , cb , cmpl_list ) ;
if ( ret )
return ret ;
2012-11-01 21:17:15 +02:00
2015-02-10 10:39:40 +02:00
if ( cb - > completed )
2016-02-07 23:35:24 +02:00
cb - > status = mei_amthif_read_start ( cl , cb - > fp ) ;
2012-11-18 15:13:17 +02:00
2012-11-01 21:17:15 +02:00
return 0 ;
}
/**
2014-09-29 16:31:50 +03:00
* mei_amthif_irq_read_msg - read routine after ISR to
2013-01-08 23:07:21 +02:00
* handle the read amthif message
2012-11-01 21:17:15 +02:00
*
2015-02-10 10:39:37 +02:00
* @ cl : mei client
2013-01-08 23:07:21 +02:00
* @ mei_hdr : header of amthif message
2015-02-10 10:39:41 +02:00
* @ cmpl_list : completed callbacks list
2012-11-01 21:17:15 +02:00
*
2015-02-10 10:39:41 +02:00
* Return : - ENODEV if cb is NULL 0 otherwise ; error message is in cb - > status
2012-11-01 21:17:15 +02:00
*/
2015-02-10 10:39:37 +02:00
int mei_amthif_irq_read_msg ( struct mei_cl * cl ,
2013-04-19 21:16:53 +03:00
struct mei_msg_hdr * mei_hdr ,
2015-02-10 10:39:41 +02:00
struct mei_cl_cb * cmpl_list )
2012-11-01 21:17:15 +02:00
{
2015-02-10 10:39:37 +02:00
struct mei_device * dev ;
2015-02-10 10:39:41 +02:00
int ret ;
2012-11-01 21:17:15 +02:00
2015-02-10 10:39:37 +02:00
dev = cl - > dev ;
2012-11-01 21:17:15 +02:00
2016-04-17 12:16:04 -04:00
if ( dev - > iamthif_state ! = MEI_IAMTHIF_READING ) {
mei_irq_discard_msg ( dev , mei_hdr ) ;
2015-02-10 10:39:41 +02:00
return 0 ;
2016-04-17 12:16:04 -04:00
}
2012-11-01 21:17:15 +02:00
2015-02-10 10:39:41 +02:00
ret = mei_cl_irq_read_msg ( cl , mei_hdr , cmpl_list ) ;
if ( ret )
return ret ;
2012-11-01 21:17:15 +02:00
if ( ! mei_hdr - > msg_complete )
return 0 ;
2014-09-29 16:31:42 +03:00
dev_dbg ( dev - > dev , " completed amthif read. \n " ) ;
2012-11-01 21:17:15 +02:00
dev - > iamthif_stall_timer = 0 ;
return 0 ;
}
/**
* mei_amthif_complete - complete amthif callback .
*
2016-02-07 23:35:26 +02:00
* @ cl : host client
2014-09-29 16:31:49 +03:00
* @ cb : callback block .
2012-11-01 21:17:15 +02:00
*/
2016-02-07 23:35:26 +02:00
void mei_amthif_complete ( struct mei_cl * cl , struct mei_cl_cb * cb )
2012-11-01 21:17:15 +02:00
{
2016-02-07 23:35:26 +02:00
struct mei_device * dev = cl - > dev ;
2015-02-10 10:39:39 +02:00
2016-05-24 16:03:36 -04:00
dev_dbg ( dev - > dev , " completing amthif call back. \n " ) ;
switch ( cb - > fop_type ) {
case MEI_FOP_WRITE :
2015-02-10 10:39:39 +02:00
if ( ! cb - > status ) {
dev - > iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER ;
2016-09-25 13:25:31 +03:00
mei_schedule_stall_timer ( dev ) ;
2015-02-10 10:39:39 +02:00
mei_io_cb_free ( cb ) ;
return ;
}
2016-05-24 16:03:36 -04:00
dev - > iamthif_state = MEI_IAMTHIF_IDLE ;
2016-06-16 17:58:57 +03:00
cl - > fp = NULL ;
2016-05-24 16:03:36 -04:00
if ( ! dev - > iamthif_canceled ) {
/*
* in case of error enqueue the write cb to complete
* read list so it can be propagated to the reader
*/
list_add_tail ( & cb - > list , & cl - > rd_completed ) ;
wake_up_interruptible ( & cl - > rx_wait ) ;
} else {
mei_io_cb_free ( cb ) ;
}
break ;
case MEI_FOP_READ :
if ( ! dev - > iamthif_canceled ) {
list_add_tail ( & cb - > list , & cl - > rd_completed ) ;
dev_dbg ( dev - > dev , " amthif read completed \n " ) ;
wake_up_interruptible ( & cl - > rx_wait ) ;
} else {
mei_io_cb_free ( cb ) ;
}
2015-02-10 10:39:39 +02:00
2016-05-24 16:03:36 -04:00
dev - > iamthif_stall_timer = 0 ;
mei_amthif_run_next_cmd ( dev ) ;
break ;
default :
WARN_ON ( 1 ) ;
2012-11-01 21:17:15 +02:00
}
}
2012-11-11 17:38:01 +02:00
/**
* mei_clear_list - removes all callbacks associated with file
* from mei_cb_list
*
* @ file : file structure
* @ mei_cb_list : callbacks list
*
* mei_clear_list is called to clear resources associated with file
* when application calls close function or Ctrl - C was pressed
*/
2016-06-16 17:58:59 +03:00
static void mei_clear_list ( const struct file * file ,
struct list_head * mei_cb_list )
2012-11-11 17:38:01 +02:00
{
2015-02-10 10:39:45 +02:00
struct mei_cl_cb * cb , * next ;
2016-05-24 16:03:36 -04:00
list_for_each_entry_safe ( cb , next , mei_cb_list , list )
if ( file = = cb - > fp )
2015-02-10 10:39:45 +02:00
mei_io_cb_free ( cb ) ;
2012-11-11 17:38:01 +02:00
}
/**
* mei_amthif_release - the release function
*
2013-04-05 01:05:05 +09:00
* @ dev : device structure
2012-11-11 17:38:01 +02:00
* @ file : pointer to file structure
*
2014-09-29 16:31:49 +03:00
* Return : 0 on success , < 0 on error
2012-11-11 17:38:01 +02:00
*/
int mei_amthif_release ( struct mei_device * dev , struct file * file )
{
2016-06-16 17:58:57 +03:00
struct mei_cl * cl = file - > private_data ;
2013-09-16 23:44:47 +03:00
if ( dev - > iamthif_open_count > 0 )
dev - > iamthif_open_count - - ;
2012-11-11 17:38:01 +02:00
2016-06-16 17:58:57 +03:00
if ( cl - > fp = = file & & dev - > iamthif_state ! = MEI_IAMTHIF_IDLE ) {
2012-11-11 17:38:01 +02:00
2014-09-29 16:31:42 +03:00
dev_dbg ( dev - > dev , " amthif canceled iamthif state %d \n " ,
2012-11-11 17:38:01 +02:00
dev - > iamthif_state ) ;
dev - > iamthif_canceled = true ;
}
2016-06-16 17:58:59 +03:00
mei_clear_list ( file , & dev - > amthif_cmd_list . list ) ;
mei_clear_list ( file , & cl - > rd_completed ) ;
mei_clear_list ( file , & dev - > ctrl_rd_list . list ) ;
2012-11-11 17:38:01 +02:00
return 0 ;
}