alistair23-linux/drivers/misc/mei/amthif.c
Alexander Usyskin e0cb6b2f87 mei: enable to set the internal flag for client write
Prepare the client write functions to set the internal flag in message
header. Carry both blocking and internal modes inside the transmit cb,
and call internal bus function  __mei_cl_send() with send mode bit mask.
The Internal flag should be added only on messages generated by the
driver.

Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2016-11-10 13:18:02 +01:00

362 lines
7.8 KiB
C

/*
*
* 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>
#include <linux/slab.h>
#include <linux/mei.h>
#include "mei_dev.h"
#include "hbm.h"
#include "client.h"
const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d,
0xac, 0xa8, 0x46, 0xe0,
0xff, 0x65, 0x81, 0x4c);
/**
* 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;
dev->iamthif_stall_timer = 0;
dev->iamthif_open_count = 0;
}
/**
* mei_amthif_host_init - mei initialization amthif client.
*
* @dev: the device structure
* @me_cl: me client
*
* Return: 0 on success, <0 on failure.
*/
int mei_amthif_host_init(struct mei_device *dev, struct mei_me_client *me_cl)
{
struct mei_cl *cl = &dev->iamthif_cl;
int ret;
mutex_lock(&dev->device_lock);
if (mei_cl_is_connected(cl)) {
ret = 0;
goto out;
}
dev->iamthif_state = MEI_IAMTHIF_IDLE;
mei_cl_init(cl, dev);
ret = mei_cl_link(cl);
if (ret < 0) {
dev_err(dev->dev, "amthif: failed cl_link %d\n", ret);
goto out;
}
ret = mei_cl_connect(cl, me_cl, NULL);
out:
mutex_unlock(&dev->device_lock);
return ret;
}
/**
* mei_amthif_read_start - queue message for sending read credential
*
* @cl: host client
* @fp: file pointer of message recipient
*
* Return: 0 on success, <0 on failure.
*/
static int mei_amthif_read_start(struct mei_cl *cl, const struct file *fp)
{
struct mei_device *dev = cl->dev;
struct mei_cl_cb *cb;
cb = mei_cl_enqueue_ctrl_wr_cb(cl, mei_cl_mtu(cl), MEI_FOP_READ, fp);
if (!cb)
return -ENOMEM;
cl->rx_flow_ctrl_creds++;
dev->iamthif_state = MEI_IAMTHIF_READING;
cl->fp = cb->fp;
return 0;
}
/**
* mei_amthif_run_next_cmd - send next amt command from queue
*
* @dev: the device structure
*
* Return: 0 on success, <0 on failure.
*/
int mei_amthif_run_next_cmd(struct mei_device *dev)
{
struct mei_cl *cl = &dev->iamthif_cl;
struct mei_cl_cb *cb;
int ret;
dev->iamthif_canceled = false;
dev_dbg(dev->dev, "complete amthif cmd_list cb.\n");
cb = list_first_entry_or_null(&dev->amthif_cmd_list.list,
typeof(*cb), list);
if (!cb) {
dev->iamthif_state = MEI_IAMTHIF_IDLE;
cl->fp = NULL;
return 0;
}
list_del_init(&cb->list);
dev->iamthif_state = MEI_IAMTHIF_WRITING;
cl->fp = cb->fp;
ret = mei_cl_write(cl, cb);
if (ret < 0)
return ret;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->fp);
return 0;
}
/**
* 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)
{
struct mei_device *dev = cl->dev;
list_add_tail(&cb->list, &dev->amthif_cmd_list.list);
/*
* The previous request is still in processing, queue this one.
*/
if (dev->iamthif_state != MEI_IAMTHIF_IDLE)
return 0;
return mei_amthif_run_next_cmd(dev);
}
/**
* 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
*/
unsigned int mei_amthif_poll(struct file *file, poll_table *wait)
{
struct mei_cl *cl = file->private_data;
struct mei_cl_cb *cb = mei_cl_read_cb(cl, file);
unsigned int mask = 0;
poll_wait(file, &cl->rx_wait, wait);
if (cb)
mask |= POLLIN | POLLRDNORM;
return mask;
}
/**
* mei_amthif_irq_write - write iamthif command in irq thread context.
*
* @cl: private data of the file object.
* @cb: callback block.
* @cmpl_list: complete list.
*
* Return: 0, OK; otherwise, error.
*/
int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb,
struct mei_cl_cb *cmpl_list)
{
int ret;
ret = mei_cl_irq_write(cl, cb, cmpl_list);
if (ret)
return ret;
if (cb->completed)
cb->status = mei_amthif_read_start(cl, cb->fp);
return 0;
}
/**
* mei_amthif_irq_read_msg - read routine after ISR to
* handle the read amthif message
*
* @cl: mei client
* @mei_hdr: header of amthif message
* @cmpl_list: completed callbacks list
*
* Return: -ENODEV if cb is NULL 0 otherwise; error message is in cb->status
*/
int mei_amthif_irq_read_msg(struct mei_cl *cl,
struct mei_msg_hdr *mei_hdr,
struct mei_cl_cb *cmpl_list)
{
struct mei_device *dev;
int ret;
dev = cl->dev;
if (dev->iamthif_state != MEI_IAMTHIF_READING) {
mei_irq_discard_msg(dev, mei_hdr);
return 0;
}
ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list);
if (ret)
return ret;
if (!mei_hdr->msg_complete)
return 0;
dev_dbg(dev->dev, "completed amthif read.\n ");
dev->iamthif_stall_timer = 0;
return 0;
}
/**
* mei_amthif_complete - complete amthif callback.
*
* @cl: host client
* @cb: callback block.
*/
void mei_amthif_complete(struct mei_cl *cl, struct mei_cl_cb *cb)
{
struct mei_device *dev = cl->dev;
dev_dbg(dev->dev, "completing amthif call back.\n");
switch (cb->fop_type) {
case MEI_FOP_WRITE:
if (!cb->status) {
dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER;
mei_schedule_stall_timer(dev);
mei_io_cb_free(cb);
return;
}
dev->iamthif_state = MEI_IAMTHIF_IDLE;
cl->fp = NULL;
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);
}
dev->iamthif_stall_timer = 0;
mei_amthif_run_next_cmd(dev);
break;
default:
WARN_ON(1);
}
}
/**
* 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
*/
static void mei_clear_list(const struct file *file,
struct list_head *mei_cb_list)
{
struct mei_cl_cb *cb, *next;
list_for_each_entry_safe(cb, next, mei_cb_list, list)
if (file == cb->fp)
mei_io_cb_free(cb);
}
/**
* mei_amthif_release - the release function
*
* @dev: device structure
* @file: pointer to file structure
*
* Return: 0 on success, <0 on error
*/
int mei_amthif_release(struct mei_device *dev, struct file *file)
{
struct mei_cl *cl = file->private_data;
if (dev->iamthif_open_count > 0)
dev->iamthif_open_count--;
if (cl->fp == file && dev->iamthif_state != MEI_IAMTHIF_IDLE) {
dev_dbg(dev->dev, "amthif canceled iamthif state %d\n",
dev->iamthif_state);
dev->iamthif_canceled = true;
}
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);
return 0;
}