/*
*
* 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/delay.h>
#include <linux/pm_runtime.h>
#include <linux/mei.h>
#include "mei_dev.h"
#include "hbm.h"
#include "client.h"
/**
* mei_me_cl_by_uuid - locate index of me client
*
* @dev: mei device
*
* Locking: called under "dev->device_lock" lock
*
* returns me client index or -ENOENT if not found
*/
int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *uuid)
{
int i;
for (i = 0; i < dev->me_clients_num; ++i)
if (uuid_le_cmp(*uuid,
dev->me_clients[i].props.protocol_name) == 0)
return i;
return -ENOENT;
}
/**
* mei_me_cl_by_id return index to me_clients for client_id
*
* @dev: the device structure
* @client_id: me client id
*
* Locking: called under "dev->device_lock" lock
*
* returns index on success, -ENOENT on failure.
*/
int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
{
int i;
for (i = 0; i < dev->me_clients_num; i++)
if (dev->me_clients[i].client_id == client_id)
return i;
return -ENOENT;
}
/**
* mei_cl_cmp_id - tells if the clients are the same
*
* @cl1: host client 1
* @cl2: host client 2
*
* returns true - if the clients has same host and me ids
* false - otherwise
*/
static inline bool mei_cl_cmp_id(const struct mei_cl *cl1,
const struct mei_cl *cl2)
{
return cl1 && cl2 &&
(cl1->host_client_id == cl2->host_client_id) &&
(cl1->me_client_id == cl2->me_client_id);
}
/**
* mei_io_list_flush - removes cbs belonging to cl.
*
* @list: an instance of our list structure
* @cl: host client, can be NULL for flushing the whole list
* @free: whether to free the cbs
*/
static void __mei_io_list_flush(struct mei_cl_cb *list,
struct mei_cl *cl, bool free)
{
struct mei_cl_cb *cb;
struct mei_cl_cb *next;
/* enable removing everything if no cl is specified */
list_for_each_entry_safe(cb, next, &list->list, list) {
if (!cl || (cb->cl && mei_cl_cmp_id(cl, cb->cl))) {
list_del(&cb->list);
if (free)
mei_io_cb_free(cb);
}
}
}
/**
* mei_io_list_flush - removes list entry belonging to cl.
*
* @list: An instance of our list structure
* @cl: host client
*/
static inline void mei_io_list_flush(struct mei_cl_cb *list, struct mei_cl *cl)
{
__mei_io_list_flush(list, cl, false);
}
/**
* mei_io_list_free - removes cb belonging to cl and free them
*
* @list: An instance of our list structure
* @cl: host client
*/
static inline void mei_io_list_free(struct mei_cl_cb *list, struct mei_cl *cl)
{
__mei_io_list_flush(list, cl, true);
}
/**
* mei_io_cb_free - free mei_cb_private related memory
*
* @cb: mei callback struct
*/
void mei_io_cb_free(struct mei_cl_cb *cb)
{
if (cb == NULL)
return;
kfree(cb->request_buffer.data);
kfree(cb->response_buffer.data);
kfree(cb);
}
/**
* mei_io_cb_init - allocate and initialize io callback
*
* @cl - mei client
* @fp: pointer to file structure
*
* returns mei_cl_cb pointer or NULL;
*/
struct mei_cl_cb *mei_io_cb_init(struct mei_cl *cl, struct file *fp)
{
struct mei_cl_cb *cb;
cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
if (!cb)
return NULL;
mei_io_list_init(cb);
cb->file_object = fp;
cb->cl = cl;
cb->buf_idx = 0;
return cb;
}
/**
* mei_io_cb_alloc_req_buf - allocate request buffer
*
* @cb: io callback structure
* @length: size of the buffer
*
* returns 0 on success
* -EINVAL if cb is NULL
* -ENOMEM if allocation failed
*/
int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
{
if (!cb)
return -EINVAL;
if (length == 0)
return 0;
cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
if