2011-04-27 10:30:12

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 0/8] staging/mei: Intel MEI Driver

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.


Attachments:
(No filename) (4.22 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-27 10:30:17

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 1/8] staging/mei: PCI device and char driver support.

contains module entries and PCI driver and char device
definitions (using file_operations, pci_driver structs).

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/iorw.c | 595 ++++++++++++++++
drivers/staging/mei/main.c | 1340 +++++++++++++++++++++++++++++++++++++
drivers/staging/mei/mei.h | 148 ++++
drivers/staging/mei/mei_version.h | 31 +
4 files changed, 2114 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/iorw.c
create mode 100644 drivers/staging/mei/main.c
create mode 100644 drivers/staging/mei/mei.h
create mode 100644 drivers/staging/mei/mei_version.h

diff --git a/drivers/staging/mei/iorw.c b/drivers/staging/mei/iorw.c
new file mode 100644
index 0000000..cd76a5b
--- /dev/null
+++ b/drivers/staging/mei/iorw.c
@@ -0,0 +1,595 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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/aio.h>
+#include <linux/pci.h>
+#include <linux/init.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 "hw.h"
+#include "mei.h"
+#include "interface.h"
+#include "mei_version.h"
+
+
+
+/**
+ * mei_ioctl_connect_client - the connect to fw client IOCTL function
+ *
+ * @dev: the device structure
+ * @data: IOCTL connect data, input and output parameters
+ * @file: private data of the file object
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int mei_ioctl_connect_client(struct file *file,
+ struct mei_connect_client_data *data)
+{
+ struct mei_device *dev;
+ struct mei_cl_cb *priv_cb;
+ struct mei_client *client;
+ struct mei_cl *file_ext;
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+ long timeout = CONNECT_TIMEOUT;
+ int i;
+ int err;
+ int rets;
+
+ file_ext = file->private_data;
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return -ENODEV;
+
+ dev = file_ext->dev;
+
+ dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n");
+
+
+ /* buffered ioctl cb */
+ priv_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+ if (!priv_cb) {
+ rets = -ENOMEM;
+ goto end;
+ }
+ INIT_LIST_HEAD(&priv_cb->cb_list);
+
+ priv_cb->major_file_operations = MEI_IOCTL;
+
+ if (dev->mei_state != MEI_ENABLED) {
+ rets = -ENODEV;
+ goto end;
+ }
+ if (file_ext->state != MEI_FILE_INITIALIZING &&
+ file_ext->state != MEI_FILE_DISCONNECTED) {
+ rets = -EBUSY;
+ goto end;
+ }
+
+ /* find ME client we're trying to connect to */
+ i = mei_find_me_client_index(dev, data->in_client_uuid);
+ if (i >= 0 && !dev->me_clients[i].props.fixed_address) {
+ file_ext->me_client_id = dev->me_clients[i].client_id;
+ file_ext->state = MEI_FILE_CONNECTING;
+ }
+
+ dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n",
+ file_ext->me_client_id);
+ dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n",
+ dev->me_clients[i].props.protocol_version);
+ dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n",
+ dev->me_clients[i].props.max_msg_length);
+
+ /* if we're connecting to amthi client so we will use the exist
+ * connection
+ */
+ if (uuid_le_cmp(data->in_client_uuid, mei_amthi_guid) == 0) {
+ dev_dbg(&dev->pdev->dev, "FW Client is amthi\n");
+ if (dev->iamthif_file_ext.state != MEI_FILE_CONNECTED) {
+ rets = -ENODEV;
+ goto end;
+ }
+ clear_bit(file_ext->host_client_id, dev->host_clients_map);
+ list_for_each_entry_safe(file_pos, file_next,
+ &dev->file_list, link) {
+ if (mei_fe_same_id(file_ext, file_pos)) {
+ dev_dbg(&dev->pdev->dev,
+ "remove file private data node host"
+ " client = %d, ME client = %d.\n",
+ file_pos->host_client_id,
+ file_pos->me_client_id);
+ list_del(&file_pos->link);
+ }
+
+ }
+ dev_dbg(&dev->pdev->dev, "free file private data memory.\n");
+ kfree(file_ext);
+
+ file_ext = NULL;
+ file->private_data = &dev->iamthif_file_ext;
+
+ client = &data->out_client_properties;
+ client->max_msg_length =
+ dev->me_clients[i].props.max_msg_length;
+ client->protocol_version =
+ dev->me_clients[i].props.protocol_version;
+ rets = dev->iamthif_file_ext.status;
+
+ goto end;
+ }
+
+ if (file_ext->state != MEI_FILE_CONNECTING) {
+ rets = -ENODEV;
+ goto end;
+ }
+
+
+ /* prepare the output buffer */
+ client = &data->out_client_properties;
+ client->max_msg_length = dev->me_clients[i].props.max_msg_length;
+ client->protocol_version = dev->me_clients[i].props.protocol_version;
+ dev_dbg(&dev->pdev->dev, "Can connect?\n");
+ if (dev->mei_host_buffer_is_empty
+ && !mei_other_client_is_connecting(dev, file_ext)) {
+ dev_dbg(&dev->pdev->dev, "Sending Connect Message\n");
+ dev->mei_host_buffer_is_empty = 0;
+ if (!mei_connect(dev, file_ext)) {
+ dev_dbg(&dev->pdev->dev, "Sending connect message - failed\n");
+ rets = -ENODEV;
+ goto end;
+ } else {
+ dev_dbg(&dev->pdev->dev, "Sending connect message - succeeded\n");
+ file_ext->timer_count = MEI_CONNECT_TIMEOUT;
+ priv_cb->file_private = file_ext;
+ list_add_tail(&priv_cb->cb_list,
+ &dev->ctrl_rd_list.mei_cb.
+ cb_list);
+ }
+
+
+ } else {
+ dev_dbg(&dev->pdev->dev, "Queuing the connect request due to device busy\n");
+ priv_cb->file_private = file_ext;
+ dev_dbg(&dev->pdev->dev, "add connect cb to control write list.\n");
+ list_add_tail(&priv_cb->cb_list,
+ &dev->ctrl_wr_list.mei_cb.cb_list);
+ }
+ mutex_unlock(&dev->device_lock);
+ err = wait_event_timeout(dev->wait_recvd_msg,
+ (MEI_FILE_CONNECTED == file_ext->state ||
+ MEI_FILE_DISCONNECTED == file_ext->state),
+ timeout * HZ);
+
+ mutex_lock(&dev->device_lock);
+ if (MEI_FILE_CONNECTED == file_ext->state) {
+ dev_dbg(&dev->pdev->dev, "successfully connected to FW client.\n");
+ rets = file_ext->status;
+ goto end;
+ } else {
+ dev_dbg(&dev->pdev->dev, "failed to connect to FW client.file_ext->state = %d.\n",
+ file_ext->state);
+ if (!err) {
+ dev_dbg(&dev->pdev->dev,
+ "wait_event_interruptible_timeout failed on client"
+ " connect message fw response message.\n");
+ }
+ rets = -EFAULT;
+
+ mei_flush_list(&dev->ctrl_rd_list, file_ext);
+ mei_flush_list(&dev->ctrl_wr_list, file_ext);
+ goto end;
+ }
+ rets = 0;
+end:
+ dev_dbg(&dev->pdev->dev, "free connect cb memory.");
+ kfree(priv_cb);
+ return rets;
+}
+
+/**
+ * find_amthi_read_list_entry - finds a amthilist entry for current file
+ *
+ * @dev: the device structure
+ * @file: pointer to file object
+ *
+ * returns returned a list entry on success, NULL on failure.
+ */
+struct mei_cl_cb *find_amthi_read_list_entry(
+ struct mei_device *dev,
+ struct file *file)
+{
+ struct mei_cl *file_ext_temp;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+
+ if (!dev->amthi_read_complete_list.status &&
+ !list_empty(&dev->amthi_read_complete_list.mei_cb.cb_list)) {
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->amthi_read_complete_list.mei_cb.cb_list, cb_list) {
+ file_ext_temp = (struct mei_cl *)
+ priv_cb_pos->file_private;
+ if (file_ext_temp &&
+ file_ext_temp == &dev->iamthif_file_ext &&
+ priv_cb_pos->file_object == file)
+ return priv_cb_pos;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * amthi_read - read data from AMTHI client
+ *
+ * @dev: the device structure
+ * @if_num: minor number
+ * @file: pointer to file object
+ * @*ubuf: pointer to user data in user space
+ * @length: data length to read
+ * @offset: data read offset
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * returns
+ * returned data length on success,
+ * zero if no data to read,
+ * negative on failure.
+ */
+int amthi_read(struct mei_device *dev, struct file *file,
+ char __user *ubuf, size_t length, loff_t *offset)
+{
+ int rets;
+ int wait_ret;
+ struct mei_cl_cb *priv_cb = NULL;
+ struct mei_cl *file_ext = file->private_data;
+ unsigned long timeout;
+ int i;
+
+ /* Only Posible if we are in timeout */
+ if (!file_ext || file_ext != &dev->iamthif_file_ext) {
+ dev_dbg(&dev->pdev->dev, "bad file ext.\n");
+ return -ETIMEDOUT;
+ }
+
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id ==
+ dev->iamthif_file_ext.me_client_id)
+ break;
+ }
+
+ if (i == dev->num_mei_me_clients) {
+ dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
+ return -ENODEV;
+ }
+ BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+
+ dev_dbg(&dev->pdev->dev, "checking amthi data\n");
+ priv_cb = find_amthi_read_list_entry(dev, file);
+
+ /* Check for if we can block or not*/
+ if (priv_cb == NULL && file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+
+ dev_dbg(&dev->pdev->dev, "waiting for amthi data\n");
+ while (priv_cb == NULL) {
+ /* unlock the Mutex */
+ mutex_unlock(&dev->device_lock);
+
+ wait_ret = wait_event_interruptible(dev->iamthif_file_ext.wait,
+ (priv_cb = find_amthi_read_list_entry(dev, file)));
+
+ if (wait_ret)
+ return -ERESTARTSYS;
+
+ dev_dbg(&dev->pdev->dev, "woke up from sleep\n");
+
+ /* Locking again the Mutex */
+ mutex_lock(&dev->device_lock);
+ }
+
+
+ dev_dbg(&dev->pdev->dev, "Got amthi data\n");
+ dev->iamthif_timer = 0;
+
+ if (priv_cb) {
+ timeout = priv_cb->read_time +
+ msecs_to_jiffies(IAMTHIF_READ_TIMER);
+ dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n",
+ timeout);
+
+ if (time_after(jiffies, timeout)) {
+ dev_dbg(&dev->pdev->dev, "amthi Time out\n");
+ /* 15 sec for the message has expired */
+ list_del(&priv_cb->cb_list);
+ rets = -ETIMEDOUT;
+ goto free;
+ }
+ }
+ /* if the whole message will fit remove it from the list */
+ if (priv_cb->information >= *offset &&
+ length >= (priv_cb->information - *offset))
+ list_del(&priv_cb->cb_list);
+ else if (priv_cb->information > 0 && priv_cb->information <= *offset) {
+ /* end of the message has been reached */
+ list_del(&priv_cb->cb_list);
+ rets = 0;
+ goto free;
+ }
+ /* else means that not full buffer will be read and do not
+ * remove message from deletion list
+ */
+
+ dev_dbg(&dev->pdev->dev, "amthi priv_cb->response_buffer size - %d\n",
+ priv_cb->response_buffer.size);
+ dev_dbg(&dev->pdev->dev, "amthi priv_cb->information - %lu\n",
+ priv_cb->information);
+
+ /* length is being turncated to PAGE_SIZE, however,
+ * the information may be longer */
+ length = min_t(size_t, length, (priv_cb->information - *offset));
+
+ if (copy_to_user(ubuf,
+ priv_cb->response_buffer.data + *offset,
+ length))
+ rets = -EFAULT;
+ else {
+ rets = length;
+ if ((*offset + length) < priv_cb->information) {
+ *offset += length;
+ goto out;
+ }
+ }
+free:
+ dev_dbg(&dev->pdev->dev, "free amthi cb memory.\n");
+ *offset = 0;
+ mei_free_cb_private(priv_cb);
+out:
+ return rets;
+}
+
+/**
+ * mei_start_read - the start read client message function.
+ *
+ * @dev: the device structure
+ * @if_num: minor number
+ * @file_ext: private data of the file object
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int mei_start_read(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ struct mei_cl_cb *priv_cb;
+ int rets = 0;
+ int i;
+
+ if (file_ext->state != MEI_FILE_CONNECTED)
+ return -ENODEV;
+
+ if (dev->mei_state != MEI_ENABLED)
+ return -ENODEV;
+
+ dev_dbg(&dev->pdev->dev, "check if read is pending.\n");
+ if (file_ext->read_pending || file_ext->read_cb) {
+ dev_dbg(&dev->pdev->dev, "read is pending.\n");
+ return -EBUSY;
+ }
+
+ priv_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+ if (!priv_cb)
+ return -ENOMEM;
+
+ dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n",
+ file_ext->host_client_id, file_ext->me_client_id);
+
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id == file_ext->me_client_id)
+ break;
+
+ }
+
+ BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+ if (i == dev->num_mei_me_clients) {
+ rets = -ENODEV;
+ goto unlock;
+ }
+
+ priv_cb->response_buffer.size = dev->me_clients[i].props.max_msg_length;
+ priv_cb->response_buffer.data =
+ kmalloc(priv_cb->response_buffer.size, GFP_KERNEL);
+ if (!priv_cb->response_buffer.data) {
+ rets = -ENOMEM;
+ goto unlock;
+ }
+ dev_dbg(&dev->pdev->dev, "allocation call back data success.\n");
+ priv_cb->major_file_operations = MEI_READ;
+ /* make sure information is zero before we start */
+ priv_cb->information = 0;
+ priv_cb->file_private = (void *) file_ext;
+ file_ext->read_cb = priv_cb;
+ if (dev->mei_host_buffer_is_empty) {
+ dev->mei_host_buffer_is_empty = 0;
+ if (!mei_send_flow_control(dev, file_ext)) {
+ rets = -ENODEV;
+ goto unlock;
+ } else {
+ list_add_tail(&priv_cb->cb_list,
+ &dev->read_list.mei_cb.cb_list);
+ }
+ } else {
+ list_add_tail(&priv_cb->cb_list,
+ &dev->ctrl_wr_list.mei_cb.cb_list);
+ }
+ return rets;
+unlock:
+ mei_free_cb_private(priv_cb);
+ return rets;
+}
+
+/**
+ * amthi_write - write iamthif data to amthi client
+ *
+ * @dev: the device structure
+ * @priv_cb: mei call back struct
+ *
+ * returns 0 on success, <0 on failure.
+ */
+int amthi_write(struct mei_device *dev,
+ struct mei_cl_cb *priv_cb)
+{
+ struct mei_msg_hdr mei_hdr;
+
+ if (!dev || !priv_cb)
+ return -ENODEV;
+
+ dev_dbg(&dev->pdev->dev, "write data to amthi client.\n");
+
+ dev->iamthif_state = MEI_IAMTHIF_WRITING;
+ dev->iamthif_current_cb = priv_cb;
+ dev->iamthif_file_object = priv_cb->file_object;
+ dev->iamthif_canceled = 0;
+ dev->iamthif_ioctl = 1;
+ dev->iamthif_msg_buf_size = priv_cb->request_buffer.size;
+ memcpy(dev->iamthif_msg_buf, priv_cb->request_buffer.data,
+ priv_cb->request_buffer.size);
+
+ if (mei_flow_ctrl_creds(dev, &dev->iamthif_file_ext) &&
+ dev->mei_host_buffer_is_empty) {
+ dev->mei_host_buffer_is_empty = 0;
+ if (priv_cb->request_buffer.size >
+ (((dev->host_hw_state & H_CBD) >> 24) * sizeof(u32))
+ -sizeof(struct mei_msg_hdr)) {
+ mei_hdr.length =
+ (((dev->host_hw_state & H_CBD) >> 24) *
+ sizeof(u32)) - sizeof(struct mei_msg_hdr);
+ mei_hdr.msg_complete = 0;
+ } else {
+ mei_hdr.length = priv_cb->request_buffer.size;
+ mei_hdr.msg_complete = 1;
+ }
+
+ mei_hdr.host_addr = dev->iamthif_file_ext.host_client_id;
+ mei_hdr.me_addr = dev->iamthif_file_ext.me_client_id;
+ mei_hdr.reserved = 0;
+ dev->iamthif_msg_buf_index += mei_hdr.length;
+ if (!mei_write_message(dev, &mei_hdr,
+ (unsigned char *)(dev->iamthif_msg_buf),
+ mei_hdr.length))
+ return -ENODEV;
+
+ if (mei_hdr.msg_complete) {
+ mei_flow_ctrl_reduce(dev, &dev->iamthif_file_ext);
+ dev->iamthif_flow_control_pending = 1;
+ dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
+ dev_dbg(&dev->pdev->dev, "add amthi cb to write waiting list\n");
+ dev->iamthif_current_cb = priv_cb;
+ dev->iamthif_file_object = priv_cb->file_object;
+ list_add_tail(&priv_cb->cb_list,
+ &dev->write_waiting_list.mei_cb.cb_list);
+ } else {
+ dev_dbg(&dev->pdev->dev, "message does not complete, "
+ "so add amthi cb to write list.\n");
+ list_add_tail(&priv_cb->cb_list,
+ &dev->write_list.mei_cb.cb_list);
+ }
+ } else {
+ if (!(dev->mei_host_buffer_is_empty))
+ dev_dbg(&dev->pdev->dev, "host buffer is not empty");
+
+ dev_dbg(&dev->pdev->dev, "No flow control credentials, "
+ "so add iamthif cb to write list.\n");
+ list_add_tail(&priv_cb->cb_list,
+ &dev->write_list.mei_cb.cb_list);
+ }
+ return 0;
+}
+
+/**
+ * iamthif_ioctl_send_msg - send cmd data to amthi client
+ *
+ * @dev: the device structure
+ *
+ * returns 0 on success, <0 on failure.
+ */
+void run_next_iamthif_cmd(struct mei_device *dev)
+{
+ struct mei_cl *file_ext_tmp;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+ int status;
+
+ if (!dev)
+ return;
+
+ dev->iamthif_msg_buf_size = 0;
+ dev->iamthif_msg_buf_index = 0;
+ dev->iamthif_canceled = 0;
+ dev->iamthif_ioctl = 1;
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ dev->iamthif_timer = 0;
+ dev->iamthif_file_object = NULL;
+
+ if (dev->amthi_cmd_list.status == 0 &&
+ !list_empty(&dev->amthi_cmd_list.mei_cb.cb_list)) {
+ dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n");
+
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->amthi_cmd_list.mei_cb.cb_list, cb_list) {
+ list_del(&priv_cb_pos->cb_list);
+ file_ext_tmp = (struct mei_cl *)
+ priv_cb_pos->file_private;
+
+ if (file_ext_tmp &&
+ file_ext_tmp == &dev->iamthif_file_ext) {
+ status = amthi_write(dev, priv_cb_pos);
+ if (status) {
+ dev_dbg(&dev->pdev->dev,
+ "amthi write failed status = %d\n",
+ status);
+ return;
+ }
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * mei_free_cb_private - free mei_cb_private related memory
+ *
+ * @priv_cb: mei callback struct
+ */
+void mei_free_cb_private(struct mei_cl_cb *priv_cb)
+{
+ if (priv_cb == NULL)
+ return;
+
+ kfree(priv_cb->request_buffer.data);
+ kfree(priv_cb->response_buffer.data);
+ kfree(priv_cb);
+}
diff --git a/drivers/staging/mei/main.c b/drivers/staging/mei/main.c
new file mode 100644
index 0000000..b63157d
--- /dev/null
+++ b/drivers/staging/mei/main.c
@@ -0,0 +1,1340 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/aio.h>
+#include <linux/pci.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/uuid.h>
+#include <linux/compat.h>
+#include <linux/jiffies.h>
+
+#include "mei.h"
+#include "interface.h"
+#include "mei_version.h"
+
+
+#define MEI_READ_TIMEOUT 45
+#define MEI_DRIVER_NAME "mei"
+#define MEI_DEV_NAME "mei"
+
+/*
+ * mei driver strings
+ */
+static char mei_driver_name[] = MEI_DRIVER_NAME;
+static const char mei_driver_string[] = "Intel(R) Management Engine Interface";
+static const char mei_driver_version[] = MEI_DRIVER_VERSION;
+
+/* mei char device for registration */
+static struct cdev mei_cdev;
+
+/* major number for device */
+static int mei_major;
+/* The device pointer */
+/* Currently this driver works as long as there is only a single AMT device. */
+static struct pci_dev *mei_device;
+
+static struct class *mei_class;
+
+
+/* mei_pci_tbl - PCI Device ID Table */
+static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82946GZ)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G35)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82Q965)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82G965)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GM965)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_82GME965)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q35)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82G33)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82Q33)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_82X38)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_3200)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_6)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_7)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_8)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_9)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9_10)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_3)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH9M_4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_3)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_ICH10_4)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_IBXPK_2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_CPT_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PBG_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
+
+ /* required last entry */
+ {0, }
+};
+
+MODULE_DEVICE_TABLE(pci, mei_pci_tbl);
+
+static DEFINE_MUTEX(mei_mutex);
+
+/**
+ * mei_probe - Device Initialization Routine
+ *
+ * @pdev: PCI device structure
+ * @ent: entry in kcs_pci_tbl
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int __devinit mei_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct mei_device *dev;
+ int err;
+
+ mutex_lock(&mei_mutex);
+ if (mei_device) {
+ err = -EEXIST;
+ goto end;
+ }
+ /* enable pci dev */
+ err = pci_enable_device(pdev);
+ if (err) {
+ printk(KERN_ERR "mei: Failed to enable pci device.\n");
+ goto end;
+ }
+ /* set PCI host mastering */
+ pci_set_master(pdev);
+ /* pci request regions for mei driver */
+ err = pci_request_regions(pdev, mei_driver_name);
+ if (err) {
+ printk(KERN_ERR "mei: Failed to get pci regions.\n");
+ goto disable_device;
+ }
+ /* allocates and initializes the mei dev structure */
+ dev = init_mei_device(pdev);
+ if (!dev) {
+ err = -ENOMEM;
+ goto release_regions;
+ }
+ /* mapping IO device memory */
+ dev->mem_addr = pci_iomap(pdev, 0, 0);
+ if (!dev->mem_addr) {
+ printk(KERN_ERR "mei: mapping I/O device memory failure.\n");
+ err = -ENOMEM;
+ goto free_device;
+ }
+ /* request and enable interrupt */
+ err = request_threaded_irq(pdev->irq,
+ mei_interrupt_quick_handler,
+ mei_interrupt_thread_handler,
+ IRQF_SHARED, mei_driver_name, dev);
+ if (err) {
+ printk(KERN_ERR "mei: request_threaded_irq failure. irq = %d\n",
+ pdev->irq);
+ goto unmap_memory;
+ }
+ INIT_DELAYED_WORK(&dev->wd_work, mei_wd_timer);
+ if (mei_hw_init(dev)) {
+ printk(KERN_ERR "mei: Init hw failure.\n");
+ err = -ENODEV;
+ goto release_irq;
+ }
+ mei_device = pdev;
+ pci_set_drvdata(pdev, dev);
+ schedule_delayed_work(&dev->wd_work, HZ);
+
+ mutex_unlock(&mei_mutex);
+
+ pr_debug("mei: Driver initialization successful.\n");
+
+ return 0;
+
+release_irq:
+ /* disable interrupts */
+ dev->host_hw_state = mei_hcsr_read(dev);
+ mei_disable_interrupts(dev);
+ flush_scheduled_work();
+ free_irq(pdev->irq, dev);
+unmap_memory:
+ iounmap(dev->mem_addr);
+free_device:
+ kfree(dev);
+release_regions:
+ pci_release_regions(pdev);
+disable_device:
+ pci_disable_device(pdev);
+end:
+ mutex_unlock(&mei_mutex);
+ printk(KERN_ERR "mei: Driver initialization failed.\n");
+ return err;
+}
+
+/**
+ * mei_remove - Device Removal Routine
+ *
+ * @pdev: PCI device structure
+ *
+ * mei_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.
+ */
+static void __devexit mei_remove(struct pci_dev *pdev)
+{
+ struct mei_device *dev;
+
+ if (mei_device != pdev)
+ return;
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return;
+
+ mutex_lock(&dev->device_lock);
+
+ mei_wd_stop(dev, false);
+
+ mei_device = NULL;
+
+ if (dev->iamthif_file_ext.state == MEI_FILE_CONNECTED) {
+ dev->iamthif_file_ext.state = MEI_FILE_DISCONNECTING;
+ mei_disconnect_host_client(dev, &dev->iamthif_file_ext);
+ }
+ if (dev->wd_cl.state == MEI_FILE_CONNECTED) {
+ dev->wd_cl.state = MEI_FILE_DISCONNECTING;
+ mei_disconnect_host_client(dev, &dev->wd_cl);
+ }
+
+ /* remove entry if already in list */
+ dev_dbg(&pdev->dev, "list del iamthif and wd file list.\n");
+ mei_remove_client_from_file_list(dev, dev->wd_cl.
+ host_client_id);
+ mei_remove_client_from_file_list(dev,
+ dev->iamthif_file_ext.host_client_id);
+
+ dev->iamthif_current_cb = NULL;
+ dev->num_mei_me_clients = 0;
+
+ mutex_unlock(&dev->device_lock);
+
+ flush_scheduled_work();
+
+ /* disable interrupts */
+ mei_disable_interrupts(dev);
+
+ free_irq(pdev->irq, dev);
+ pci_set_drvdata(pdev, NULL);
+
+ if (dev->mem_addr)
+ iounmap(dev->mem_addr);
+
+ kfree(dev);
+
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * mei_clear_list - removes all callbacks associated with file
+ * from mei_cb_list
+ *
+ * @dev: device structure.
+ * @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
+ *
+ * returns true if callback removed from the list, false otherwise
+ */
+static bool mei_clear_list(struct mei_device *dev,
+ struct file *file, struct list_head *mei_cb_list)
+{
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+ struct file *file_temp;
+ bool removed = false;
+
+ /* list all list member */
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ mei_cb_list, cb_list) {
+ file_temp = (struct file *)priv_cb_pos->file_object;
+ /* check if list member associated with a file */
+ if (file_temp == file) {
+ /* remove member from the list */
+ list_del(&priv_cb_pos->cb_list);
+ /* check if cb equal to current iamthif cb */
+ if (dev->iamthif_current_cb == priv_cb_pos) {
+ dev->iamthif_current_cb = NULL;
+ /* send flow control to iamthif client */
+ mei_send_flow_control(dev,
+ &dev->iamthif_file_ext);
+ }
+ /* free all allocated buffers */
+ mei_free_cb_private(priv_cb_pos);
+ priv_cb_pos = NULL;
+ removed = true;
+ }
+ }
+ return removed;
+}
+
+/**
+ * mei_clear_lists - removes all callbacks associated with file
+ *
+ * @dev: device structure
+ * @file: file structure
+ *
+ * mei_clear_lists is called to clear resources associated with file
+ * when application calls close function or Ctrl-C was pressed
+ *
+ * returns true if callback removed from the list, false otherwise
+ */
+static bool mei_clear_lists(struct mei_device *dev, struct file *file)
+{
+ bool removed = false;
+
+ /* remove callbacks associated with a file */
+ mei_clear_list(dev, file, &dev->amthi_cmd_list.mei_cb.cb_list);
+ if (mei_clear_list(dev, file,
+ &dev->amthi_read_complete_list.mei_cb.cb_list))
+ removed = true;
+
+ mei_clear_list(dev, file, &dev->ctrl_rd_list.mei_cb.cb_list);
+
+ if (mei_clear_list(dev, file, &dev->ctrl_wr_list.mei_cb.cb_list))
+ removed = true;
+
+ if (mei_clear_list(dev, file,
+ &dev->write_waiting_list.mei_cb.cb_list))
+ removed = true;
+
+ if (mei_clear_list(dev, file, &dev->write_list.mei_cb.cb_list))
+ removed = true;
+
+ /* check if iamthif_current_cb not NULL */
+ if (dev->iamthif_current_cb && !removed) {
+ /* check file and iamthif current cb association */
+ if (dev->iamthif_current_cb->file_object == file) {
+ /* remove cb */
+ mei_free_cb_private(dev->iamthif_current_cb);
+ dev->iamthif_current_cb = NULL;
+ removed = true;
+ }
+ }
+ return removed;
+}
+/**
+ * find_read_list_entry - find read list entry
+ *
+ * @dev: device structure
+ * @file: pointer to file structure
+ *
+ * returns cb on success, NULL on error
+ */
+static struct mei_cl_cb *find_read_list_entry(
+ struct mei_device *dev,
+ struct mei_cl *file_ext)
+{
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+ struct mei_cl *file_ext_list_temp;
+
+ if (!dev->read_list.status &&
+ !list_empty(&dev->read_list.mei_cb.cb_list)) {
+
+ dev_dbg(&dev->pdev->dev, "remove read_list CB\n");
+ list_for_each_entry_safe(priv_cb_pos,
+ priv_cb_next,
+ &dev->read_list.mei_cb.cb_list, cb_list) {
+
+ file_ext_list_temp = (struct mei_cl *)
+ priv_cb_pos->file_private;
+
+ if (file_ext_list_temp &&
+ mei_fe_same_id(file_ext, file_ext_list_temp))
+ return priv_cb_pos;
+
+ }
+ }
+ return NULL;
+}
+
+/**
+ * mei_open - the open function
+ *
+ * @inode: pointer to inode structure
+ * @file: pointer to file structure
+ *
+ * returns 0 on success, <0 on error
+ */
+static int mei_open(struct inode *inode, struct file *file)
+{
+ struct mei_cl *file_ext;
+ int if_num = iminor(inode), err;
+ struct mei_device *dev;
+
+ err = -ENODEV;
+ if (!mei_device)
+ goto out;
+
+ dev = pci_get_drvdata(mei_device);
+ if (if_num != MEI_MINOR_NUMBER || !dev)
+ goto out;
+
+ mutex_lock(&dev->device_lock);
+ err = -ENOMEM;
+ file_ext = mei_alloc_file_private(dev);
+ if (!file_ext)
+ goto out;
+
+ err = -ENODEV;
+ if (dev->mei_state != MEI_ENABLED) {
+ dev_dbg(&dev->pdev->dev, "mei_state != MEI_ENABLED mei_state= %d\n",
+ dev->mei_state);
+ goto out_unlock;
+ }
+ err = -EMFILE;
+ if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT)
+ goto out_unlock;
+
+ file_ext->host_client_id = find_first_zero_bit(dev->host_clients_map,
+ MEI_CLIENTS_MAX);
+ if (file_ext->host_client_id > MEI_CLIENTS_MAX)
+ goto out_unlock;
+
+ dev_dbg(&dev->pdev->dev, "client_id = %d\n", file_ext->host_client_id);
+
+ dev->open_handle_count++;
+ list_add_tail(&file_ext->link, &dev->file_list);
+
+ set_bit(file_ext->host_client_id, dev->host_clients_map);
+ file_ext->state = MEI_FILE_INITIALIZING;
+ file_ext->sm_state = 0;
+
+ file->private_data = file_ext;
+ mutex_unlock(&dev->device_lock);
+
+ return 0;
+
+out_unlock:
+ mutex_unlock(&dev->device_lock);
+ kfree(file_ext);
+out:
+ return err;
+}
+
+/**
+ * mei_release - the release function
+ *
+ * @inode: pointer to inode structure
+ * @file: pointer to file structure
+ *
+ * returns 0 on success, <0 on error
+ */
+static int mei_release(struct inode *inode, struct file *file)
+{
+ struct mei_cl *file_ext = file->private_data;
+ struct mei_cl_cb *priv_cb;
+ struct mei_device *dev;
+ int rets = 0;
+
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return -ENODEV;
+
+ dev = file_ext->dev;
+
+ mutex_lock(&dev->device_lock);
+ if (file_ext != &dev->iamthif_file_ext) {
+ if (file_ext->state == MEI_FILE_CONNECTED) {
+ file_ext->state = MEI_FILE_DISCONNECTING;
+ dev_dbg(&dev->pdev->dev,
+ "disconnecting client host client = %d, "
+ "ME client = %d\n",
+ file_ext->host_client_id,
+ file_ext->me_client_id);
+ rets = mei_disconnect_host_client(dev, file_ext);
+ }
+ mei_flush_queues(dev, file_ext);
+ dev_dbg(&dev->pdev->dev, "remove client host client = %d, ME client = %d\n",
+ file_ext->host_client_id,
+ file_ext->me_client_id);
+
+ if (dev->open_handle_count > 0) {
+ clear_bit(file_ext->host_client_id,
+ dev->host_clients_map);
+ dev->open_handle_count--;
+ }
+ mei_remove_client_from_file_list(dev, file_ext->host_client_id);
+
+ /* free read cb */
+ priv_cb = NULL;
+ if (file_ext->read_cb) {
+ priv_cb = find_read_list_entry(dev, file_ext);
+ /* Remove entry from read list */
+ if (priv_cb)
+ list_del(&priv_cb->cb_list);
+
+ priv_cb = file_ext->read_cb;
+ file_ext->read_cb = NULL;
+ }
+
+ file->private_data = NULL;
+
+ if (priv_cb) {
+ mei_free_cb_private(priv_cb);
+ priv_cb = NULL;
+ }
+
+ kfree(file_ext);
+ } else {
+ if (dev->open_handle_count > 0)
+ dev->open_handle_count--;
+
+ if (dev->iamthif_file_object == file &&
+ dev->iamthif_state != MEI_IAMTHIF_IDLE) {
+
+ dev_dbg(&dev->pdev->dev, "amthi canceled iamthif state %d\n",
+ dev->iamthif_state);
+ dev->iamthif_canceled = 1;
+ if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) {
+ dev_dbg(&dev->pdev->dev, "run next amthi iamthif cb\n");
+ run_next_iamthif_cmd(dev);
+ }
+ }
+
+ if (mei_clear_lists(dev, file))
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+
+ }
+ mutex_unlock(&dev->device_lock);
+ return rets;
+}
+
+
+/**
+ * mei_read - the read function.
+ *
+ * @file: pointer to file structure
+ * @ubuf: pointer to user buffer
+ * @length: buffer length
+ * @offset: data offset in buffer
+ *
+ * returns >=0 data length on success , <0 on error
+ */
+static ssize_t mei_read(struct file *file, char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct mei_cl *file_ext = file->private_data;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb = NULL;
+ struct mei_device *dev;
+ int i;
+ int rets;
+ int err;
+
+
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return -ENODEV;
+
+ dev = file_ext->dev;
+
+ mutex_lock(&dev->device_lock);
+ if (dev->mei_state != MEI_ENABLED) {
+ rets = -ENODEV;
+ goto out;
+ }
+
+ if ((file_ext->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
+ /* Do not allow to read watchdog client */
+ i = mei_find_me_client_index(dev, mei_wd_guid);
+ if (i >= 0) {
+ struct mei_me_client *me_client = &dev->me_clients[i];
+
+ if (file_ext->me_client_id == me_client->client_id) {
+ rets = -EBADF;
+ goto out;
+ }
+ }
+ } else {
+ file_ext->sm_state &= ~MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
+ }
+
+ if (file_ext == &dev->iamthif_file_ext) {
+ rets = amthi_read(dev, file, ubuf, length, offset);
+ goto out;
+ }
+
+ if (file_ext->read_cb && file_ext->read_cb->information > *offset) {
+ priv_cb = file_ext->read_cb;
+ goto copy_buffer;
+ } else if (file_ext->read_cb && file_ext->read_cb->information > 0 &&
+ file_ext->read_cb->information <= *offset) {
+ priv_cb = file_ext->read_cb;
+ rets = 0;
+ goto free;
+ } else if ((!file_ext->read_cb || !file_ext->read_cb->information) &&
+ *offset > 0) {
+ /*Offset needs to be cleaned for contingous reads*/
+ *offset = 0;
+ rets = 0;
+ goto out;
+ }
+
+ err = mei_start_read(dev, file_ext);
+ if (err && err != -EBUSY) {
+ dev_dbg(&dev->pdev->dev,
+ "mei start read failure with status = %d\n", err);
+ rets = err;
+ goto out;
+ }
+
+ if (MEI_READ_COMPLETE != file_ext->reading_state &&
+ !waitqueue_active(&file_ext->rx_wait)) {
+ if (file->f_flags & O_NONBLOCK) {
+ rets = -EAGAIN;
+ goto out;
+ }
+
+ mutex_unlock(&dev->device_lock);
+
+ if (wait_event_interruptible(file_ext->rx_wait,
+ (MEI_READ_COMPLETE == file_ext->reading_state ||
+ MEI_FILE_INITIALIZING == file_ext->state ||
+ MEI_FILE_DISCONNECTED == file_ext->state ||
+ MEI_FILE_DISCONNECTING == file_ext->state))) {
+ if (signal_pending(current))
+ return -EINTR;
+ return -ERESTARTSYS;
+ }
+
+ mutex_lock(&dev->device_lock);
+ if (MEI_FILE_INITIALIZING == file_ext->state ||
+ MEI_FILE_DISCONNECTED == file_ext->state ||
+ MEI_FILE_DISCONNECTING == file_ext->state) {
+ rets = -EBUSY;
+ goto out;
+ }
+ }
+
+ priv_cb = file_ext->read_cb;
+
+ if (!priv_cb) {
+ rets = -ENODEV;
+ goto out;
+ }
+ if (file_ext->reading_state != MEI_READ_COMPLETE) {
+ rets = 0;
+ goto out;
+ }
+ /* now copy the data to user space */
+copy_buffer:
+ dev_dbg(&dev->pdev->dev, "priv_cb->response_buffer size - %d\n",
+ priv_cb->response_buffer.size);
+ dev_dbg(&dev->pdev->dev, "priv_cb->information - %lu\n",
+ priv_cb->information);
+ if (length == 0 || ubuf == NULL || *offset > priv_cb->information) {
+ rets = -EMSGSIZE;
+ goto free;
+ }
+
+ /* length is being turncated to PAGE_SIZE, however, */
+ /* information size may be longer */
+ length = min_t(size_t, length, (priv_cb->information - *offset));
+
+ if (copy_to_user(ubuf,
+ priv_cb->response_buffer.data + *offset,
+ length)) {
+ rets = -EFAULT;
+ goto free;
+ }
+
+ rets = length;
+ *offset += length;
+ if ((unsigned long)*offset < priv_cb->information)
+ goto out;
+
+free:
+ priv_cb_pos = find_read_list_entry(dev, file_ext);
+ /* Remove entry from read list */
+ if (priv_cb_pos)
+ list_del(&priv_cb_pos->cb_list);
+ mei_free_cb_private(priv_cb);
+ file_ext->reading_state = MEI_IDLE;
+ file_ext->read_cb = NULL;
+ file_ext->read_pending = 0;
+out:
+ dev_dbg(&dev->pdev->dev, "end mei read rets= %d\n", rets);
+ mutex_unlock(&dev->device_lock);
+ return rets;
+}
+
+/**
+ * mei_write - the write function.
+ *
+ * @file: pointer to file structure
+ * @ubuf: pointer to user buffer
+ * @length: buffer length
+ * @offset: data offset in buffer
+ *
+ * returns >=0 data length on success , <0 on error
+ */
+static ssize_t mei_write(struct file *file, const char __user *ubuf,
+ size_t length, loff_t *offset)
+{
+ struct mei_cl *file_ext = file->private_data;
+ struct mei_cl_cb *priv_write_cb = NULL;
+ struct mei_msg_hdr mei_hdr;
+ struct mei_device *dev;
+ unsigned long timeout = 0;
+ int rets;
+ int i;
+
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return -ENODEV;
+
+ dev = file_ext->dev;
+
+ mutex_lock(&dev->device_lock);
+
+ if (dev->mei_state != MEI_ENABLED) {
+ mutex_unlock(&dev->device_lock);
+ return -ENODEV;
+ }
+
+ if (file_ext == &dev->iamthif_file_ext) {
+ priv_write_cb = find_amthi_read_list_entry(dev, file);
+
+ if (priv_write_cb) {
+ timeout = priv_write_cb->read_time +
+ msecs_to_jiffies(IAMTHIF_READ_TIMER);
+
+ if (time_after(jiffies, timeout) ||
+ file_ext->reading_state == MEI_READ_COMPLETE) {
+ *offset = 0;
+ list_del(&priv_write_cb->cb_list);
+ mei_free_cb_private(priv_write_cb);
+ priv_write_cb = NULL;
+ }
+ }
+ }
+
+ /* free entry used in read */
+ if (file_ext->reading_state == MEI_READ_COMPLETE) {
+ *offset = 0;
+ priv_write_cb = find_read_list_entry(dev, file_ext);
+ if (priv_write_cb) {
+ list_del(&priv_write_cb->cb_list);
+ mei_free_cb_private(priv_write_cb);
+ priv_write_cb = NULL;
+ file_ext->reading_state = MEI_IDLE;
+ file_ext->read_cb = NULL;
+ file_ext->read_pending = 0;
+ }
+ } else if (file_ext->reading_state == MEI_IDLE &&
+ !file_ext->read_pending)
+ *offset = 0;
+
+
+ priv_write_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+ if (!priv_write_cb) {
+ mutex_unlock(&dev->device_lock);
+ return -ENOMEM;
+ }
+
+ priv_write_cb->file_object = file;
+ priv_write_cb->file_private = file_ext;
+ priv_write_cb->request_buffer.data = kmalloc(length, GFP_KERNEL);
+ rets = -ENOMEM;
+ if (!priv_write_cb->request_buffer.data)
+ goto unlock_dev;
+
+ dev_dbg(&dev->pdev->dev, "length =%d\n", (int) length);
+
+ rets = -EFAULT;
+ if (copy_from_user(priv_write_cb->request_buffer.data, ubuf, length))
+ goto unlock_dev;
+
+ file_ext->sm_state = 0;
+ if (length == 4 &&
+ ((memcmp(mei_wd_state_independence_msg[0],
+ priv_write_cb->request_buffer.data, 4) == 0) ||
+ (memcmp(mei_wd_state_independence_msg[1],
+ priv_write_cb->request_buffer.data, 4) == 0) ||
+ (memcmp(mei_wd_state_independence_msg[2],
+ priv_write_cb->request_buffer.data, 4) == 0)))
+ file_ext->sm_state |= MEI_WD_STATE_INDEPENDENCE_MSG_SENT;
+
+ INIT_LIST_HEAD(&priv_write_cb->cb_list);
+ if (file_ext == &dev->iamthif_file_ext) {
+ priv_write_cb->response_buffer.data =
+ kmalloc(dev->iamthif_mtu, GFP_KERNEL);
+ if (!priv_write_cb->response_buffer.data) {
+ rets = -ENOMEM;
+ goto unlock_dev;
+ }
+ if (dev->mei_state != MEI_ENABLED) {
+ rets = -ENODEV;
+ goto unlock_dev;
+ }
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id ==
+ dev->iamthif_file_ext.me_client_id)
+ break;
+ }
+
+ BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+ if (i == dev->num_mei_me_clients ||
+ (dev->me_clients[i].client_id !=
+ dev->iamthif_file_ext.me_client_id)) {
+ rets = -ENODEV;
+ goto unlock_dev;
+ } else if (length > dev->me_clients[i].props.max_msg_length ||
+ length <= 0) {
+ rets = -EMSGSIZE;
+ goto unlock_dev;
+ }
+
+ priv_write_cb->response_buffer.size = dev->iamthif_mtu;
+ priv_write_cb->major_file_operations = MEI_IOCTL;
+ priv_write_cb->information = 0;
+ priv_write_cb->request_buffer.size = length;
+ if (dev->iamthif_file_ext.state != MEI_FILE_CONNECTED) {
+ rets = -ENODEV;
+ goto unlock_dev;
+ }
+
+ if (!list_empty(&dev->amthi_cmd_list.mei_cb.cb_list) ||
+ dev->iamthif_state != MEI_IAMTHIF_IDLE) {
+ dev_dbg(&dev->pdev->dev, "amthi_state = %d\n",
+ (int) dev->iamthif_state);
+ dev_dbg(&dev->pdev->dev, "add amthi cb to amthi cmd waiting list\n");
+ list_add_tail(&priv_write_cb->cb_list,
+ &dev->amthi_cmd_list.mei_cb.cb_list);
+ rets = length;
+ } else {
+ dev_dbg(&dev->pdev->dev, "call amthi write\n");
+ rets = amthi_write(dev, priv_write_cb);
+
+ if (rets) {
+ dev_dbg(&dev->pdev->dev, "amthi write failed with status = %d\n",
+ rets);
+ goto unlock_dev;
+ }
+ rets = length;
+ }
+ mutex_unlock(&dev->device_lock);
+ return rets;
+ }
+
+ priv_write_cb->major_file_operations = MEI_WRITE;
+ /* make sure information is zero before we start */
+
+ priv_write_cb->information = 0;
+ priv_write_cb->request_buffer.size = length;
+
+ dev_dbg(&dev->pdev->dev, "host client = %d, ME client = %d\n",
+ file_ext->host_client_id, file_ext->me_client_id);
+ if (file_ext->state != MEI_FILE_CONNECTED) {
+ rets = -ENODEV;
+ dev_dbg(&dev->pdev->dev, "host client = %d, is not connected to ME client = %d",
+ file_ext->host_client_id,
+ file_ext->me_client_id);
+ goto unlock_dev;
+ }
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id ==
+ file_ext->me_client_id)
+ break;
+ }
+ BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id);
+ if (i == dev->num_mei_me_clients) {
+ rets = -ENODEV;
+ goto unlock_dev;
+ }
+ if (length > dev->me_clients[i].props.max_msg_length || length <= 0) {
+ rets = -EINVAL;
+ goto unlock_dev;
+ }
+ priv_write_cb->file_private = file_ext;
+
+ if (mei_flow_ctrl_creds(dev, file_ext) &&
+ dev->mei_host_buffer_is_empty) {
+ dev->mei_host_buffer_is_empty = 0;
+ if (length > ((((dev->host_hw_state & H_CBD) >> 24) *
+ sizeof(u32)) - sizeof(struct mei_msg_hdr))) {
+
+ mei_hdr.length =
+ (((dev->host_hw_state & H_CBD) >> 24) *
+ sizeof(u32)) -
+ sizeof(struct mei_msg_hdr);
+ mei_hdr.msg_complete = 0;
+ } else {
+ mei_hdr.length = length;
+ mei_hdr.msg_complete = 1;
+ }
+ mei_hdr.host_addr = file_ext->host_client_id;
+ mei_hdr.me_addr = file_ext->me_client_id;
+ mei_hdr.reserved = 0;
+ dev_dbg(&dev->pdev->dev, "call mei_write_message header=%08x.\n",
+ *((u32 *) &mei_hdr));
+ if (!mei_write_message(dev, &mei_hdr,
+ (unsigned char *) (priv_write_cb->request_buffer.data),
+ mei_hdr.length)) {
+ rets = -ENODEV;
+ goto unlock_dev;
+ }
+ file_ext->writing_state = MEI_WRITING;
+ priv_write_cb->information = mei_hdr.length;
+ if (mei_hdr.msg_complete) {
+ mei_flow_ctrl_reduce(dev, file_ext);
+ list_add_tail(&priv_write_cb->cb_list,
+ &dev->write_waiting_list.mei_cb.cb_list);
+ } else {
+ list_add_tail(&priv_write_cb->cb_list,
+ &dev->write_list.mei_cb.cb_list);
+ }
+
+ } else {
+
+ priv_write_cb->information = 0;
+ file_ext->writing_state = MEI_WRITING;
+ list_add_tail(&priv_write_cb->cb_list,
+ &dev->write_list.mei_cb.cb_list);
+ }
+ mutex_unlock(&dev->device_lock);
+ return length;
+
+unlock_dev:
+ mutex_unlock(&dev->device_lock);
+ mei_free_cb_private(priv_write_cb);
+ return rets;
+}
+
+
+/**
+ * mei_ioctl - the IOCTL function
+ *
+ * @file: pointer to file structure
+ * @cmd: ioctl command
+ * @data: pointer to mei message structure
+ *
+ * returns 0 on success , <0 on error
+ */
+static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
+{
+ struct mei_device *dev;
+ struct mei_cl *file_ext = file->private_data;
+ struct mei_connect_client_data *connect_data = NULL;
+ int rets;
+
+ if (cmd != IOCTL_MEI_CONNECT_CLIENT)
+ return -EINVAL;
+
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return -ENODEV;
+
+ dev = file_ext->dev;
+
+ dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
+
+ mutex_lock(&dev->device_lock);
+ if (dev->mei_state != MEI_ENABLED) {
+ rets = -ENODEV;
+ goto out;
+ }
+
+ dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
+
+ connect_data = kzalloc(sizeof(struct mei_connect_client_data),
+ GFP_KERNEL);
+ if (!connect_data) {
+ rets = -ENOMEM;
+ goto out;
+ }
+ dev_dbg(&dev->pdev->dev, "copy connect data from user\n");
+ if (copy_from_user(connect_data, (char __user *)data,
+ sizeof(struct mei_connect_client_data))) {
+ dev_dbg(&dev->pdev->dev, "failed to copy data from userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+ rets = mei_ioctl_connect_client(file, connect_data);
+
+ /* if all is ok, copying the data back to user. */
+ if (rets)
+ goto out;
+
+ dev_dbg(&dev->pdev->dev, "copy connect data to user\n");
+ if (copy_to_user((char __user *)data, connect_data,
+ sizeof(struct mei_connect_client_data))) {
+ dev_dbg(&dev->pdev->dev, "failed to copy data to userland\n");
+ rets = -EFAULT;
+ goto out;
+ }
+
+out:
+ kfree(connect_data);
+ mutex_unlock(&dev->device_lock);
+ return rets;
+}
+
+/**
+ * mei_compat_ioctl - the compat IOCTL function
+ *
+ * @file: pointer to file structure
+ * @cmd: ioctl command
+ * @data: pointer to mei message structure
+ *
+ * returns 0 on success , <0 on error
+ */
+#ifdef CONFIG_COMPAT
+static long mei_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long data)
+{
+ return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data));
+}
+#endif
+
+
+/**
+ * mei_poll - the poll function
+ *
+ * @file: pointer to file structure
+ * @wait: pointer to poll_table structure
+ *
+ * returns poll mask
+ */
+static unsigned int mei_poll(struct file *file, poll_table *wait)
+{
+ struct mei_cl *file_ext = file->private_data;
+ struct mei_device *dev;
+ unsigned int mask = 0;
+
+ if (WARN_ON(!file_ext || !file_ext->dev))
+ return mask;
+
+ dev = file_ext->dev;
+
+ mutex_lock(&dev->device_lock);
+
+ if (dev->mei_state != MEI_ENABLED)
+ goto out;
+
+
+ if (file_ext == &dev->iamthif_file_ext) {
+ mutex_unlock(&dev->device_lock);
+ poll_wait(file, &dev->iamthif_file_ext.wait, wait);
+ mutex_lock(&dev->device_lock);
+ if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE &&
+ dev->iamthif_file_object == file) {
+ mask |= (POLLIN | POLLRDNORM);
+ dev_dbg(&dev->pdev->dev, "run next amthi cb\n");
+ run_next_iamthif_cmd(dev);
+ }
+ goto out;
+ }
+
+ mutex_unlock(&dev->device_lock);
+ poll_wait(file, &file_ext->tx_wait, wait);
+ mutex_lock(&dev->device_lock);
+ if (MEI_WRITE_COMPLETE == file_ext->writing_state)
+ mask |= (POLLIN | POLLRDNORM);
+
+out:
+ mutex_unlock(&dev->device_lock);
+ return mask;
+}
+
+#ifdef CONFIG_PM
+static int mei_pci_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev = pci_get_drvdata(pdev);
+ int err;
+
+ if (!dev)
+ return -ENODEV;
+ mutex_lock(&dev->device_lock);
+ /* Stop watchdog if exists */
+ err = mei_wd_stop(dev, true);
+ /* Set new mei state */
+ if (dev->mei_state == MEI_ENABLED ||
+ dev->mei_state == MEI_RECOVERING_FROM_RESET) {
+ dev->mei_state = MEI_POWER_DOWN;
+ mei_reset(dev, 0);
+ }
+ mutex_unlock(&dev->device_lock);
+
+ free_irq(pdev->irq, dev);
+
+
+ return err;
+}
+
+static int mei_pci_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct mei_device *dev;
+ int err;
+
+ dev = pci_get_drvdata(pdev);
+ if (!dev)
+ return -ENODEV;
+
+ /* request and enable interrupt */
+ err = request_threaded_irq(pdev->irq,
+ mei_interrupt_quick_handler,
+ mei_interrupt_thread_handler,
+ IRQF_SHARED, mei_driver_name, dev);
+ if (err) {
+ printk(KERN_ERR "mei: Request_irq failure. irq = %d\n",
+ pdev->irq);
+ return err;
+ }
+
+ mutex_lock(&dev->device_lock);
+ dev->mei_state = MEI_POWER_UP;
+ mei_reset(dev, 1);
+ mutex_unlock(&dev->device_lock);
+
+ /* Start watchdog if stopped in suspend */
+ if (dev->wd_timeout) {
+ mei_wd_start_setup(dev);
+ dev->wd_due_counter = 1;
+ schedule_delayed_work(&dev->wd_work, HZ);
+ }
+ return err;
+}
+static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
+#define MEI_PM_OPS (&mei_pm_ops)
+#else
+#define MIE_PM_OPS NULL
+#endif /* CONFIG_PM */
+/*
+ * PCI driver structure
+ */
+static struct pci_driver mei_driver = {
+ .name = mei_driver_name,
+ .id_table = mei_pci_tbl,
+ .probe = mei_probe,
+ .remove = __devexit_p(mei_remove),
+ .shutdown = __devexit_p(mei_remove),
+ .driver.pm = MEI_PM_OPS,
+};
+
+/*
+ * file operations structure will be used for mei char device.
+ */
+static const struct file_operations mei_fops = {
+ .owner = THIS_MODULE,
+ .read = mei_read,
+ .unlocked_ioctl = mei_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = mei_compat_ioctl,
+#endif
+ .open = mei_open,
+ .release = mei_release,
+ .write = mei_write,
+ .poll = mei_poll,
+};
+
+/**
+ * mei_registration_cdev - sets up the cdev structure for mei device.
+ *
+ * @dev: char device struct
+ * @hminor: minor number for registration char device
+ * @fops: file operations structure
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_registration_cdev(struct cdev *dev, int hminor,
+ const struct file_operations *fops)
+{
+ int ret, devno = MKDEV(mei_major, hminor);
+
+ cdev_init(dev, fops);
+ dev->owner = THIS_MODULE;
+ ret = cdev_add(dev, devno, 1);
+ /* Fail gracefully if need be */
+ if (ret)
+ printk(KERN_ERR "mei: Error %d registering mei device %d\n",
+ ret, hminor);
+ return ret;
+}
+
+/**
+ * mei_register_cdev - registers mei char device
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_register_cdev(void)
+{
+ int ret;
+ dev_t dev;
+
+ /* registration of char devices */
+ ret = alloc_chrdev_region(&dev, MEI_MINORS_BASE, MEI_MINORS_COUNT,
+ MEI_DRIVER_NAME);
+ if (ret) {
+ printk(KERN_ERR "mei: Error allocating char device region.\n");
+ return ret;
+ }
+
+ mei_major = MAJOR(dev);
+
+ ret = mei_registration_cdev(&mei_cdev, MEI_MINOR_NUMBER,
+ &mei_fops);
+ if (ret)
+ unregister_chrdev_region(MKDEV(mei_major, MEI_MINORS_BASE),
+ MEI_MINORS_COUNT);
+
+ return ret;
+}
+
+/**
+ * mei_unregister_cdev - unregisters mei char device
+ */
+static void mei_unregister_cdev(void)
+{
+ cdev_del(&mei_cdev);
+ unregister_chrdev_region(MKDEV(mei_major, MEI_MINORS_BASE),
+ MEI_MINORS_COUNT);
+}
+
+/**
+ * mei_sysfs_device_create - adds device entry to sysfs
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_sysfs_device_create(void)
+{
+ struct class *class;
+ void *tmphdev;
+ int err;
+
+ class = class_create(THIS_MODULE, MEI_DRIVER_NAME);
+ if (IS_ERR(class)) {
+ err = PTR_ERR(class);
+ printk(KERN_ERR "mei: Error creating mei class.\n");
+ goto err_out;
+ }
+
+ tmphdev = device_create(class, NULL, mei_cdev.dev, NULL,
+ MEI_DEV_NAME);
+ if (IS_ERR(tmphdev)) {
+ err = PTR_ERR(tmphdev);
+ goto err_destroy;
+ }
+
+ mei_class = class;
+ return 0;
+
+err_destroy:
+ class_destroy(class);
+err_out:
+ return err;
+}
+
+/**
+ * mei_sysfs_device_remove - unregisters the device entry on sysfs
+ */
+static void mei_sysfs_device_remove(void)
+{
+ if (IS_ERR_OR_NULL(mei_class))
+ return;
+
+ device_destroy(mei_class, mei_cdev.dev);
+ class_destroy(mei_class);
+}
+
+/**
+ * mei_init_module - Driver Registration Routine
+ *
+ * mei_init_module is the first routine called when the driver is
+ * loaded. All it does is to register with the PCI subsystem.
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int __init mei_init_module(void)
+{
+ int ret;
+
+ pr_debug("mei: %s - version %s\n",
+ mei_driver_string, mei_driver_version);
+ /* init pci module */
+ ret = pci_register_driver(&mei_driver);
+ if (ret < 0) {
+ printk(KERN_ERR "mei: Error registering driver.\n");
+ goto end;
+ }
+
+ ret = mei_register_cdev();
+ if (ret)
+ goto unregister_pci;
+
+ ret = mei_sysfs_device_create();
+ if (ret)
+ goto unregister_cdev;
+
+ return ret;
+
+unregister_cdev:
+ mei_unregister_cdev();
+unregister_pci:
+ pci_unregister_driver(&mei_driver);
+end:
+ return ret;
+}
+
+module_init(mei_init_module);
+
+/**
+ * mei_exit_module - Driver Exit Cleanup Routine
+ *
+ * mei_exit_module is called just before the driver is removed
+ * from memory.
+ */
+static void __exit mei_exit_module(void)
+{
+ pci_unregister_driver(&mei_driver);
+ mei_sysfs_device_remove();
+ mei_unregister_cdev();
+
+ pr_debug("mei: Driver unloaded successfully.\n");
+}
+
+module_exit(mei_exit_module);
+
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) Management Engine Interface");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(MEI_DRIVER_VERSION);
diff --git a/drivers/staging/mei/mei.h b/drivers/staging/mei/mei.h
new file mode 100644
index 0000000..d570b80
--- /dev/null
+++ b/drivers/staging/mei/mei.h
@@ -0,0 +1,148 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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.
+ *
+ */
+
+
+#ifndef _MEI_H_
+#define _MEI_H_
+
+#include <linux/mei.h>
+#include <linux/interrupt.h>
+#include "hw.h"
+
+
+#define CONNECT_TIMEOUT 15 /* HPS definition */
+#define INIT_CLIENTS_TIMEOUT 15 /* HPS definition */
+
+extern const uuid_le mei_amthi_guid;
+extern const uuid_le mei_wd_guid;
+extern const u8 mei_wd_state_independence_msg[3][4];
+
+/*
+ * mei device ID
+ */
+#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */
+#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */
+#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */
+#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */
+
+#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */
+#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */
+
+#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */
+#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */
+#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */
+#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */
+#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */
+
+#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */
+
+#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */
+
+#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */
+
+#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */
+#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */
+
+#define MEI_DEV_ID_CPT_1 0x1C3A /* Cougerpoint */
+#define MEI_DEV_ID_PBG_1 0x1D3A /* PBG */
+
+#define MEI_DEV_ID_PPT_1 0x1E3A /* Pantherpoint PPT */
+#define MEI_DEV_ID_PPT_2 0x1CBA /* Pantherpoint PPT */
+#define MEI_DEV_ID_PPT_3 0x1DBA /* Pantherpoint PPT */
+
+
+/*
+ * mei init function prototypes
+ */
+struct mei_device *init_mei_device(struct pci_dev *pdev);
+void mei_reset(struct mei_device *dev, int interrupts);
+int mei_hw_init(struct mei_device *dev);
+int mei_task_initialize_clients(void *data);
+int mei_initialize_clients(struct mei_device *dev);
+struct mei_cl *mei_alloc_file_private(struct mei_device *dev);
+int mei_disconnect_host_client(struct mei_device *dev, struct mei_cl *file_ext);
+void mei_initialize_list(struct mei_io_list *list,
+ struct mei_device *dev);
+void mei_flush_list(struct mei_io_list *list, struct mei_cl *file_ext);
+void mei_flush_queues(struct mei_device *dev, struct mei_cl *file_ext);
+
+void mei_remove_client_from_file_list(struct mei_device *dev,
+ u8 host_client_id);
+void host_start_message(struct mei_device *dev);
+void host_enum_clients_message(struct mei_device *dev);
+void allocate_me_clients_storage(struct mei_device *dev);
+void host_client_properties(struct mei_device *dev);
+void host_init_iamthif(struct mei_device *dev);
+void mei_init_file_private(struct mei_cl *priv, struct mei_device *dev);
+
+u8 mei_find_me_client_update_filext(struct mei_device *dev,
+ struct mei_cl *priv,
+ const uuid_le *cguid, u8 client_id);
+/*
+ * interrupt functions prototype
+ */
+irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id);
+irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id);
+void mei_wd_timer(struct work_struct *work);
+
+/*
+ * input output function prototype
+ */
+int mei_ioctl_connect_client(struct file *file,
+ struct mei_connect_client_data *data);
+
+int mei_start_read(struct mei_device *dev, struct mei_cl *file_ext);
+
+int amthi_write(struct mei_device *dev, struct mei_cl_cb *priv_cb);
+
+int amthi_read(struct mei_device *dev, struct file *file,
+ char __user *ubuf, size_t length, loff_t *offset);
+
+struct mei_cl_cb *find_amthi_read_list_entry(struct mei_device *dev,
+ struct file *file);
+
+void run_next_iamthif_cmd(struct mei_device *dev);
+
+void mei_free_cb_private(struct mei_cl_cb *priv_cb);
+
+int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid);
+
+
+/**
+ * mei_fe_same_id - tells if file private data have same id
+ *
+ * @fe1: private data of 1. file object
+ * @fe2: private data of 2. file object
+ *
+ * returns !=0 - if ids are the same, 0 - if differ.
+ */
+static inline int mei_fe_same_id(const struct mei_cl *fe1,
+ const struct mei_cl *fe2)
+{
+ return ((fe1->host_client_id == fe2->host_client_id) &&
+ (fe1->me_client_id == fe2->me_client_id));
+}
+#endif /* _MEI_H_ */
diff --git a/drivers/staging/mei/mei_version.h b/drivers/staging/mei/mei_version.h
new file mode 100644
index 0000000..075bad8
--- /dev/null
+++ b/drivers/staging/mei/mei_version.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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.
+ *
+ */
+
+
+#ifndef MEI_VERSION_H
+#define MEI_VERSION_H
+
+#define MAJOR_VERSION 7
+#define MINOR_VERSION 1
+#define QUICK_FIX_NUMBER 20
+#define VER_BUILD 1
+
+#define MEI_DRV_VER1 __stringify(MAJOR_VERSION) "." __stringify(MINOR_VERSION)
+#define MEI_DRV_VER2 __stringify(QUICK_FIX_NUMBER) "." __stringify(VER_BUILD)
+
+#define MEI_DRIVER_VERSION MEI_DRV_VER1 "." MEI_DRV_VER2
+
+#endif
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:30:19

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 2/8] staging/mei: Interrupt handling.

ISR and interrupt thread for handling incoming data.
e.g. read bus message, read client message, handle reset requests.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/interrupt.c | 1634 +++++++++++++++++++++++++++++++++++++++
1 files changed, 1634 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/interrupt.c

diff --git a/drivers/staging/mei/interrupt.c b/drivers/staging/mei/interrupt.c
new file mode 100644
index 0000000..c4c3d48
--- /dev/null
+++ b/drivers/staging/mei/interrupt.c
@@ -0,0 +1,1634 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/jiffies.h>
+
+#include "mei.h"
+#include "hw.h"
+#include "interface.h"
+
+
+/**
+ * mei_interrupt_quick_handler - The ISR of the MEI device
+ *
+ * @irq: The irq number
+ * @dev_id: pointer to the device structure
+ *
+ * returns irqreturn_t
+ */
+irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id)
+{
+ struct mei_device *dev = (struct mei_device *) dev_id;
+ u32 csr_reg = mei_hcsr_read(dev);
+
+ if ((csr_reg & H_IS) != H_IS)
+ return IRQ_NONE;
+
+ /* clear H_IS bit in H_CSR */
+ mei_reg_write(dev, H_CSR, csr_reg);
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * _mei_cmpl - processes completed operation.
+ *
+ * @file_ext: private data of the file object.
+ * @priv_cb_pos: callback block.
+ */
+static void _mei_cmpl(struct mei_cl *file_ext, struct mei_cl_cb *priv_cb_pos)
+{
+ if (priv_cb_pos->major_file_operations == MEI_WRITE) {
+ mei_free_cb_private(priv_cb_pos);
+ priv_cb_pos = NULL;
+ file_ext->writing_state = MEI_WRITE_COMPLETE;
+ if (waitqueue_active(&file_ext->tx_wait))
+ wake_up_interruptible(&file_ext->tx_wait);
+
+ } else if (priv_cb_pos->major_file_operations == MEI_READ &&
+ MEI_READING == file_ext->reading_state) {
+ file_ext->reading_state = MEI_READ_COMPLETE;
+ if (waitqueue_active(&file_ext->rx_wait))
+ wake_up_interruptible(&file_ext->rx_wait);
+
+ }
+}
+
+/**
+ * _mei_cmpl_iamthif - processes completed iamthif operation.
+ *
+ * @dev: the device structure.
+ * @priv_cb_pos: callback block.
+ */
+static void _mei_cmpl_iamthif(struct mei_device *dev,
+ struct mei_cl_cb *priv_cb_pos)
+{
+ if (dev->iamthif_canceled != 1) {
+ dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
+ dev->iamthif_stall_timer = 0;
+ memcpy(priv_cb_pos->response_buffer.data,
+ dev->iamthif_msg_buf,
+ dev->iamthif_msg_buf_index);
+ list_add_tail(&priv_cb_pos->cb_list,
+ &dev->amthi_read_complete_list.mei_cb.cb_list);
+ dev_dbg(&dev->pdev->dev, "amthi read completed.\n");
+ dev->iamthif_timer = jiffies;
+ dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
+ dev->iamthif_timer);
+ } else {
+ run_next_iamthif_cmd(dev);
+ }
+
+ dev_dbg(&dev->pdev->dev, "completing amthi call back.\n");
+ wake_up_interruptible(&dev->iamthif_file_ext.wait);
+}
+
+
+/**
+ * mei_irq_thread_read_amthi_message - bottom half read routine after ISR to
+ * handle the read amthi message data processing.
+ *
+ * @complete_list: An instance of our list structure
+ * @dev: the device structure
+ * @mei_hdr: header of amthi message
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list,
+ struct mei_device *dev,
+ struct mei_msg_hdr *mei_hdr)
+{
+ struct mei_cl *file_ext;
+ struct mei_cl_cb *priv_cb;
+ unsigned char *buffer;
+
+ BUG_ON(mei_hdr->me_addr != dev->iamthif_file_ext.me_client_id);
+ BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
+
+ buffer = (unsigned char *) (dev->iamthif_msg_buf +
+ dev->iamthif_msg_buf_index);
+ BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
+
+ mei_read_slots(dev, buffer, mei_hdr->length);
+
+ dev->iamthif_msg_buf_index += mei_hdr->length;
+
+ if (!mei_hdr->msg_complete)
+ return 0;
+
+ dev_dbg(&dev->pdev->dev,
+ "amthi_message_buffer_index =%d\n",
+ mei_hdr->length);
+
+ dev_dbg(&dev->pdev->dev, "completed amthi read.\n ");
+ if (!dev->iamthif_current_cb)
+ return -ENODEV;
+
+ priv_cb = dev->iamthif_current_cb;
+ dev->iamthif_current_cb = NULL;
+
+ file_ext = (struct mei_cl *)priv_cb->file_private;
+ if (!file_ext)
+ return -ENODEV;
+
+ dev->iamthif_stall_timer = 0;
+ priv_cb->information = dev->iamthif_msg_buf_index;
+ priv_cb->read_time = jiffies;
+ if (dev->iamthif_ioctl && file_ext == &dev->iamthif_file_ext) {
+ /* found the iamthif cb */
+ dev_dbg(&dev->pdev->dev, "complete the amthi read cb.\n ");
+ dev_dbg(&dev->pdev->dev, "add the amthi read cb to complete.\n ");
+ list_add_tail(&priv_cb->cb_list,
+ &complete_list->mei_cb.cb_list);
+ }
+ return 0;
+}
+
+/**
+ * _mei_irq_thread_state_ok - checks if mei header matches file private data
+ *
+ * @file_ext: private data of the file object
+ * @mei_hdr: header of mei client message
+ *
+ * returns !=0 if matches, 0 if no match.
+ */
+static int _mei_irq_thread_state_ok(struct mei_cl *file_ext,
+ struct mei_msg_hdr *mei_hdr)
+{
+ return (file_ext->host_client_id == mei_hdr->host_addr &&
+ file_ext->me_client_id == mei_hdr->me_addr &&
+ file_ext->state == MEI_FILE_CONNECTED &&
+ MEI_READ_COMPLETE != file_ext->reading_state);
+}
+
+/**
+ * mei_irq_thread_read_client_message - bottom half read routine after ISR to
+ * handle the read mei client message data processing.
+ *
+ * @complete_list: An instance of our list structure
+ * @dev: the device structure
+ * @mei_hdr: header of mei client message
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
+ struct mei_device *dev,
+ struct mei_msg_hdr *mei_hdr)
+{
+ struct mei_cl *file_ext;
+ struct mei_cl_cb *priv_cb_pos = NULL, *priv_cb_next = NULL;
+ unsigned char *buffer;
+
+ dev_dbg(&dev->pdev->dev, "start client msg\n");
+ if (!(dev->read_list.status == 0 &&
+ !list_empty(&dev->read_list.mei_cb.cb_list)))
+ goto quit;
+
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->read_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)priv_cb_pos->file_private;
+ if (file_ext && _mei_irq_thread_state_ok(file_ext, mei_hdr)) {
+ file_ext->reading_state = MEI_READING;
+ buffer = (unsigned char *)
+ (priv_cb_pos->response_buffer.data +
+ priv_cb_pos->information);
+ BUG_ON(priv_cb_pos->response_buffer.size <
+ mei_hdr->length +
+ priv_cb_pos->information);
+
+ if (priv_cb_pos->response_buffer.size <
+ mei_hdr->length +
+ priv_cb_pos->information) {
+ dev_dbg(&dev->pdev->dev, "message overflow.\n");
+ list_del(&priv_cb_pos->cb_list);
+ return -ENOMEM;
+ }
+ if (buffer)
+ mei_read_slots(dev, buffer, mei_hdr->length);
+
+ priv_cb_pos->information += mei_hdr->length;
+ if (mei_hdr->msg_complete) {
+ file_ext->status = 0;
+ list_del(&priv_cb_pos->cb_list);
+ dev_dbg(&dev->pdev->dev,
+ "completed read host client = %d,"
+ "ME client = %d, "
+ "data length = %lu\n",
+ file_ext->host_client_id,
+ file_ext->me_client_id,
+ priv_cb_pos->information);
+
+ *(priv_cb_pos->response_buffer.data +
+ priv_cb_pos->information) = '\0';
+ dev_dbg(&dev->pdev->dev, "priv_cb_pos->res_buffer - %s\n",
+ priv_cb_pos->response_buffer.data);
+ list_add_tail(&priv_cb_pos->cb_list,
+ &complete_list->mei_cb.cb_list);
+ }
+
+ break;
+ }
+
+ }
+
+quit:
+ dev_dbg(&dev->pdev->dev, "message read\n");
+ if (!buffer) {
+ mei_read_slots(dev, (unsigned char *) dev->rd_msg_buf,
+ mei_hdr->length);
+ dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n",
+ *(u32 *) dev->rd_msg_buf);
+ }
+
+ return 0;
+}
+
+/**
+ * _mei_irq_thread_iamthif_read - prepares to read iamthif data.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots)
+{
+
+ if (((*slots) * sizeof(u32)) >= (sizeof(struct mei_msg_hdr)
+ + sizeof(struct hbm_flow_control))) {
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_flow_control) + 3) / 4;
+ if (!mei_send_flow_control(dev, &dev->iamthif_file_ext)) {
+ dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");
+ } else {
+ dev_dbg(&dev->pdev->dev, "iamthif flow control success\n");
+ dev->iamthif_state = MEI_IAMTHIF_READING;
+ dev->iamthif_flow_control_pending = 0;
+ dev->iamthif_msg_buf_index = 0;
+ dev->iamthif_msg_buf_size = 0;
+ dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER;
+ dev->mei_host_buffer_is_empty =
+ mei_host_buffer_is_empty(dev);
+ }
+ return 0;
+ } else {
+ return -EMSGSIZE;
+ }
+}
+
+/**
+ * _mei_irq_thread_close - processes close related operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @priv_cb_pos: callback block.
+ * @file_ext: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
+ struct mei_cl_cb *priv_cb_pos,
+ struct mei_cl *file_ext,
+ struct mei_io_list *cmpl_list)
+{
+ if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_client_disconnect_request))) {
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_client_disconnect_request) + 3) / 4;
+
+ if (!mei_disconnect(dev, file_ext)) {
+ file_ext->status = 0;
+ priv_cb_pos->information = 0;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &cmpl_list->mei_cb.cb_list);
+ return -EMSGSIZE;
+ } else {
+ file_ext->state = MEI_FILE_DISCONNECTING;
+ file_ext->status = 0;
+ priv_cb_pos->information = 0;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &dev->ctrl_rd_list.mei_cb.cb_list);
+ file_ext->timer_count = MEI_CONNECT_TIMEOUT;
+ }
+ } else {
+ /* return the cancel routine */
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/**
+ * is_treat_specially_client - checks if the message belongs
+ * to the file private data.
+ *
+ * @file_ext: private data of the file object
+ * @rs: connect response bus message
+ *
+ */
+static bool is_treat_specially_client(struct mei_cl *file_ext,
+ struct hbm_client_connect_response *rs)
+{
+
+ if (file_ext->host_client_id == rs->host_addr &&
+ file_ext->me_client_id == rs->me_addr) {
+ if (!rs->status) {
+ file_ext->state = MEI_FILE_CONNECTED;
+ file_ext->status = 0;
+
+ } else {
+ file_ext->state = MEI_FILE_DISCONNECTED;
+ file_ext->status = -ENODEV;
+ }
+ file_ext->timer_count = 0;
+
+ return true;
+ }
+ return false;
+}
+
+/**
+ * mei_client_connect_response - connects to response irq routine
+ *
+ * @dev: the device structure
+ * @rs: connect response bus message
+ */
+static void mei_client_connect_response(struct mei_device *dev,
+ struct hbm_client_connect_response *rs)
+{
+
+ struct mei_cl *file_ext;
+ struct mei_cl_cb *priv_cb_pos = NULL, *priv_cb_next = NULL;
+
+ BUG_ON(rs == NULL);
+
+ dev_dbg(&dev->pdev->dev,
+ "connect_response:\n"
+ "ME Client = %d\n"
+ "Host Client = %d\n"
+ "Status = %d\n",
+ rs->me_addr,
+ rs->host_addr,
+ rs->status);
+
+ /* if WD or iamthif client treat specially */
+
+ if (is_treat_specially_client(&(dev->wd_cl), rs)) {
+ dev_dbg(&dev->pdev->dev,
+ "dev->wd_timeout =%d.\n",
+ dev->wd_timeout);
+
+ dev->wd_due_counter = (dev->wd_timeout) ? 1 : 0;
+
+ dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
+ host_init_iamthif(dev);
+ return;
+ }
+
+ if (is_treat_specially_client(&(dev->iamthif_file_ext), rs)) {
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ return;
+ }
+ if (!dev->ctrl_rd_list.status &&
+ !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) {
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)
+ priv_cb_pos->file_private;
+ if (!file_ext) {
+ list_del(&priv_cb_pos->cb_list);
+ return;
+ }
+ if (MEI_IOCTL == priv_cb_pos->major_file_operations) {
+ if (is_treat_specially_client(file_ext, rs)) {
+ list_del(&priv_cb_pos->cb_list);
+ file_ext->status = 0;
+ file_ext->timer_count = 0;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * mei_client_disconnect_response - disconnects from response irq routine
+ *
+ * @dev: the device structure
+ * @rs: disconnect response bus message
+ */
+static void mei_client_disconnect_response(struct mei_device *dev,
+ struct hbm_client_connect_response *rs)
+{
+ struct mei_cl *file_ext;
+ struct mei_cl_cb *priv_cb_pos = NULL, *priv_cb_next = NULL;
+
+ BUG_ON(rs == NULL);
+
+ dev_dbg(&dev->pdev->dev,
+ "disconnect_response:\n"
+ "ME Client = %d\n"
+ "Host Client = %d\n"
+ "Status = %d\n",
+ rs->me_addr,
+ rs->host_addr,
+ rs->status);
+
+ if (!dev->ctrl_rd_list.status &&
+ !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) {
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)
+ priv_cb_pos->file_private;
+
+ if (!file_ext) {
+ list_del(&priv_cb_pos->cb_list);
+ return;
+ }
+
+ dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
+ if (file_ext->host_client_id == rs->host_addr &&
+ file_ext->me_client_id == rs->me_addr) {
+
+ list_del(&priv_cb_pos->cb_list);
+ if (!rs->status)
+ file_ext->state = MEI_FILE_DISCONNECTED;
+
+ file_ext->status = 0;
+ file_ext->timer_count = 0;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * same_flow_addr - tells if they have the same address.
+ *
+ * @file: private data of the file object.
+ * @flow: flow control.
+ *
+ * returns !=0, same; 0,not.
+ */
+static int same_flow_addr(struct mei_cl *file, struct hbm_flow_control *flow)
+{
+ return (file->host_client_id == flow->host_addr &&
+ file->me_client_id == flow->me_addr);
+}
+
+/**
+ * add_single_flow_creds - adds single buffer credentials.
+ *
+ * @file: private data ot the file object.
+ * @flow: flow control.
+ */
+static void add_single_flow_creds(struct mei_device *dev,
+ struct hbm_flow_control *flow)
+{
+ struct mei_me_client *client;
+ int i;
+
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ client = &dev->me_clients[i];
+ if (client && flow->me_addr == client->client_id) {
+ if (client->props.single_recv_buf) {
+ client->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",
+ client->mei_flow_ctrl_creds);
+ } else {
+ BUG(); /* error in flow control */
+ }
+ }
+ }
+}
+
+/**
+ * mei_client_flow_control_response - flow control response irq routine
+ *
+ * @dev: the device structure
+ * @flow_control: flow control response bus message
+ */
+static void mei_client_flow_control_response(struct mei_device *dev,
+ struct hbm_flow_control *flow_control)
+{
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+
+ if (!flow_control->host_addr) {
+ /* single receive buffer */
+ add_single_flow_creds(dev, flow_control);
+ } else {
+ /* normal connection */
+ list_for_each_entry_safe(file_pos, file_next,
+ &dev->file_list, link) {
+ dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
+
+ dev_dbg(&dev->pdev->dev, "file_ext of host client %d ME client %d.\n",
+ file_pos->host_client_id,
+ file_pos->me_client_id);
+ dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
+ flow_control->host_addr,
+ flow_control->me_addr);
+ if (same_flow_addr(file_pos, flow_control)) {
+ dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
+ flow_control->host_addr,
+ flow_control->me_addr);
+ file_pos->mei_flow_ctrl_creds++;
+ dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
+ file_pos->mei_flow_ctrl_creds);
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * same_disconn_addr - tells if they have the same address
+ *
+ * @file: private data of the file object.
+ * @disconn: disconnection request.
+ *
+ * returns !=0, same; 0,not.
+ */
+static int same_disconn_addr(struct mei_cl *file,
+ struct hbm_client_disconnect_request *disconn)
+{
+ return (file->host_client_id == disconn->host_addr &&
+ file->me_client_id == disconn->me_addr);
+}
+
+/**
+ * mei_client_disconnect_request - disconnects from request irq routine
+ *
+ * @dev: the device structure.
+ * @disconnect_req: disconnect request bus message.
+ */
+static void mei_client_disconnect_request(struct mei_device *dev,
+ struct hbm_client_disconnect_request *disconnect_req)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_client_connect_response *disconnect_res;
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+
+ list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
+ if (same_disconn_addr(file_pos, disconnect_req)) {
+ dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
+ disconnect_req->host_addr,
+ disconnect_req->me_addr);
+ file_pos->state = MEI_FILE_DISCONNECTED;
+ file_pos->timer_count = 0;
+ if (file_pos == &dev->wd_cl) {
+ dev->wd_due_counter = 0;
+ dev->wd_pending = 0;
+ } else if (file_pos == &dev->iamthif_file_ext)
+ dev->iamthif_timer = 0;
+
+ /* prepare disconnect response */
+ mei_hdr =
+ (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length =
+ sizeof(struct hbm_client_connect_response);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ disconnect_res =
+ (struct hbm_client_connect_response *)
+ &dev->ext_msg_buf[1];
+ disconnect_res->host_addr = file_pos->host_client_id;
+ disconnect_res->me_addr = file_pos->me_client_id;
+ *(u8 *) (&disconnect_res->cmd) =
+ CLIENT_DISCONNECT_RES_CMD;
+ disconnect_res->status = 0;
+ dev->extra_write_index = 2;
+ break;
+ }
+ }
+}
+
+
+/**
+ * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
+ * handle the read bus message cmd processing.
+ *
+ * @dev: the device structure
+ * @mei_hdr: header of bus message
+ */
+static void mei_irq_thread_read_bus_message(struct mei_device *dev,
+ struct mei_msg_hdr *mei_hdr)
+{
+ struct mei_bus_message *mei_msg;
+ struct hbm_host_version_response *version_res;
+ struct hbm_client_connect_response *connect_res;
+ struct hbm_client_connect_response *disconnect_res;
+ struct hbm_flow_control *flow_control;
+ struct hbm_props_response *props_res;
+ struct hbm_host_enum_response *enum_res;
+ struct hbm_client_disconnect_request *disconnect_req;
+ struct hbm_host_stop_request *host_stop_req;
+
+ unsigned char *buffer;
+
+ /* read the message to our buffer */
+ buffer = (unsigned char *) dev->rd_msg_buf;
+ BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf));
+ mei_read_slots(dev, buffer, mei_hdr->length);
+ mei_msg = (struct mei_bus_message *) buffer;
+
+ switch (*(u8 *) mei_msg) {
+ case HOST_START_RES_CMD:
+ version_res = (struct hbm_host_version_response *) mei_msg;
+ if (version_res->host_version_supported) {
+ dev->version.major_version = HBM_MAJOR_VERSION;
+ dev->version.minor_version = HBM_MINOR_VERSION;
+ if (dev->mei_state == MEI_INIT_CLIENTS &&
+ dev->init_clients_state == MEI_START_MESSAGE) {
+ dev->init_clients_timer = 0;
+ host_enum_clients_message(dev);
+ } else {
+ dev->recvd_msg = 0;
+ dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
+ mei_reset(dev, 1);
+ return;
+ }
+ } else {
+ dev->version = version_res->me_max_version;
+ /* send stop message */
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_host_stop_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ host_stop_req = (struct hbm_host_stop_request *)
+ &dev->wr_msg_buf[1];
+
+ memset(host_stop_req,
+ 0,
+ sizeof(struct hbm_host_stop_request));
+ host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
+ host_stop_req->reason = DRIVER_STOP_REQUEST;
+ mei_write_message(dev, mei_hdr,
+ (unsigned char *) (host_stop_req),
+ mei_hdr->length);
+ dev_dbg(&dev->pdev->dev, "version mismatch.\n");
+ return;
+ }
+
+ dev->recvd_msg = 1;
+ dev_dbg(&dev->pdev->dev, "host start response message received.\n");
+ break;
+
+ case CLIENT_CONNECT_RES_CMD:
+ connect_res =
+ (struct hbm_client_connect_response *) mei_msg;
+ mei_client_connect_response(dev, connect_res);
+ dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
+ wake_up(&dev->wait_recvd_msg);
+ break;
+
+ case CLIENT_DISCONNECT_RES_CMD:
+ disconnect_res =
+ (struct hbm_client_connect_response *) mei_msg;
+ mei_client_disconnect_response(dev, disconnect_res);
+ dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
+ wake_up(&dev->wait_recvd_msg);
+ break;
+
+ case MEI_FLOW_CONTROL_CMD:
+ flow_control = (struct hbm_flow_control *) mei_msg;
+ mei_client_flow_control_response(dev, flow_control);
+ dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
+ break;
+
+ case HOST_CLIENT_PROPERTIES_RES_CMD:
+ props_res = (struct hbm_props_response *)mei_msg;
+ if (props_res->status || !dev->me_clients) {
+ dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
+ mei_reset(dev, 1);
+ return;
+ }
+ if (dev->me_clients[dev->me_client_presentation_num]
+ .client_id == props_res->address) {
+
+ dev->me_clients[dev->me_client_presentation_num].props
+ = props_res->client_properties;
+
+ if (dev->mei_state == MEI_INIT_CLIENTS &&
+ dev->init_clients_state ==
+ MEI_CLIENT_PROPERTIES_MESSAGE) {
+ dev->me_client_index++;
+ dev->me_client_presentation_num++;
+ host_client_properties(dev);
+ } else {
+ dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message");
+ mei_reset(dev, 1);
+ return;
+ }
+ } else {
+ dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n");
+ mei_reset(dev, 1);
+ return;
+ }
+ break;
+
+ case HOST_ENUM_RES_CMD:
+ enum_res = (struct hbm_host_enum_response *) mei_msg;
+ memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
+ if (dev->mei_state == MEI_INIT_CLIENTS &&
+ dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
+ dev->init_clients_timer = 0;
+ dev->me_client_presentation_num = 0;
+ dev->me_client_index = 0;
+ allocate_me_clients_storage(dev);
+ dev->init_clients_state =
+ MEI_CLIENT_PROPERTIES_MESSAGE;
+ host_client_properties(dev);
+ } else {
+ dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
+ mei_reset(dev, 1);
+ return;
+ }
+ break;
+
+ case HOST_STOP_RES_CMD:
+ dev->mei_state = MEI_DISABLED;
+ dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
+ mei_reset(dev, 1);
+ break;
+
+ case CLIENT_DISCONNECT_REQ_CMD:
+ /* search for client */
+ disconnect_req =
+ (struct hbm_client_disconnect_request *) mei_msg;
+ mei_client_disconnect_request(dev, disconnect_req);
+ break;
+
+ case ME_STOP_REQ_CMD:
+ /* prepare stop request */
+ mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_host_stop_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+ host_stop_req =
+ (struct hbm_host_stop_request *) &dev->ext_msg_buf[1];
+ memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
+ host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
+ host_stop_req->reason = DRIVER_STOP_REQUEST;
+ host_stop_req->reserved[0] = 0;
+ host_stop_req->reserved[1] = 0;
+ dev->extra_write_index = 2;
+ break;
+
+ default:
+ BUG();
+ break;
+
+ }
+}
+
+
+/**
+ * _mei_hb_read - processes read related operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @priv_cb_pos: callback block.
+ * @file_ext: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
+ struct mei_cl_cb *priv_cb_pos,
+ struct mei_cl *file_ext,
+ struct mei_io_list *cmpl_list)
+{
+ if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_flow_control))) {
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_flow_control) + 3) / 4;
+ if (!mei_send_flow_control(dev, file_ext)) {
+ file_ext->status = -ENODEV;
+ priv_cb_pos->information = 0;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &cmpl_list->mei_cb.cb_list);
+ return -ENODEV;
+ } else {
+ list_move_tail(&priv_cb_pos->cb_list,
+ &dev->read_list.mei_cb.cb_list);
+ }
+ } else {
+ /* return the cancel routine */
+ list_del(&priv_cb_pos->cb_list);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+
+/**
+ * _mei_irq_thread_ioctl - processes ioctl related operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @priv_cb_pos: callback block.
+ * @file_ext: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
+ struct mei_cl_cb *priv_cb_pos,
+ struct mei_cl *file_ext,
+ struct mei_io_list *cmpl_list)
+{
+ if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_client_connect_request))) {
+ file_ext->state = MEI_FILE_CONNECTING;
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ sizeof(struct hbm_client_connect_request) + 3) / 4;
+ if (!mei_connect(dev, file_ext)) {
+ file_ext->status = -ENODEV;
+ priv_cb_pos->information = 0;
+ list_del(&priv_cb_pos->cb_list);
+ return -ENODEV;
+ } else {
+ list_move_tail(&priv_cb_pos->cb_list,
+ &dev->ctrl_rd_list.mei_cb.cb_list);
+ file_ext->timer_count = MEI_CONNECT_TIMEOUT;
+ }
+ } else {
+ /* return the cancel routine */
+ list_del(&priv_cb_pos->cb_list);
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/**
+ * _mei_irq_thread_cmpl - processes completed and no-iamthif operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @priv_cb_pos: callback block.
+ * @file_ext: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots,
+ struct mei_cl_cb *priv_cb_pos,
+ struct mei_cl *file_ext,
+ struct mei_io_list *cmpl_list)
+{
+ struct mei_msg_hdr *mei_hdr;
+
+ if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
+ (priv_cb_pos->request_buffer.size -
+ priv_cb_pos->information))) {
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = file_ext->host_client_id;
+ mei_hdr->me_addr = file_ext->me_client_id;
+ mei_hdr->length = priv_cb_pos->request_buffer.size -
+ priv_cb_pos->information;
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+ dev_dbg(&dev->pdev->dev, "priv_cb_pos->request_buffer.size =%d"
+ "mei_hdr->msg_complete = %d\n",
+ priv_cb_pos->request_buffer.size,
+ mei_hdr->msg_complete);
+ dev_dbg(&dev->pdev->dev, "priv_cb_pos->information =%lu\n",
+ priv_cb_pos->information);
+ dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
+ mei_hdr->length);
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ mei_hdr->length + 3) / 4;
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *)
+ (priv_cb_pos->request_buffer.data +
+ priv_cb_pos->information),
+ mei_hdr->length)) {
+ file_ext->status = -ENODEV;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &cmpl_list->mei_cb.cb_list);
+ return -ENODEV;
+ } else {
+ mei_flow_ctrl_reduce(dev, file_ext);
+ file_ext->status = 0;
+ priv_cb_pos->information += mei_hdr->length;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &dev->write_waiting_list.mei_cb.cb_list);
+ }
+ } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
+ /* buffer is still empty */
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = file_ext->host_client_id;
+ mei_hdr->me_addr = file_ext->me_client_id;
+ mei_hdr->length =
+ (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
+ mei_hdr->msg_complete = 0;
+ mei_hdr->reserved = 0;
+
+ (*slots) -= (sizeof(struct mei_msg_hdr) +
+ mei_hdr->length + 3) / 4;
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *)
+ (priv_cb_pos->request_buffer.data +
+ priv_cb_pos->information),
+ mei_hdr->length)) {
+ file_ext->status = -ENODEV;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &cmpl_list->mei_cb.cb_list);
+ return -ENODEV;
+ } else {
+ priv_cb_pos->information += mei_hdr->length;
+ dev_dbg(&dev->pdev->dev,
+ "priv_cb_pos->request_buffer.size =%d"
+ " mei_hdr->msg_complete = %d\n",
+ priv_cb_pos->request_buffer.size,
+ mei_hdr->msg_complete);
+ dev_dbg(&dev->pdev->dev, "priv_cb_pos->information =%lu\n",
+ priv_cb_pos->information);
+ dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
+ mei_hdr->length);
+ }
+ return -EMSGSIZE;
+ } else {
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/**
+ * _mei_irq_thread_cmpl_iamthif - processes completed iamthif operation.
+ *
+ * @dev: the device structure.
+ * @slots: free slots.
+ * @priv_cb_pos: callback block.
+ * @file_ext: private data of the file object.
+ * @cmpl_list: complete list.
+ *
+ * returns 0, OK; otherwise, error.
+ */
+static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots,
+ struct mei_cl_cb *priv_cb_pos,
+ struct mei_cl *file_ext,
+ struct mei_io_list *cmpl_list)
+{
+ struct mei_msg_hdr *mei_hdr;
+
+ if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
+ dev->iamthif_msg_buf_size -
+ dev->iamthif_msg_buf_index)) {
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = file_ext->host_client_id;
+ mei_hdr->me_addr = file_ext->me_client_id;
+ mei_hdr->length = dev->iamthif_msg_buf_size -
+ dev->iamthif_msg_buf_index;
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ mei_hdr->length + 3) / 4;
+
+ if (!mei_write_message(dev, mei_hdr,
+ (dev->iamthif_msg_buf +
+ dev->iamthif_msg_buf_index),
+ mei_hdr->length)) {
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ file_ext->status = -ENODEV;
+ list_del(&priv_cb_pos->cb_list);
+ return -ENODEV;
+ } else {
+ mei_flow_ctrl_reduce(dev, file_ext);
+ dev->iamthif_msg_buf_index += mei_hdr->length;
+ priv_cb_pos->information = dev->iamthif_msg_buf_index;
+ file_ext->status = 0;
+ dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
+ dev->iamthif_flow_control_pending = 1;
+ /* save iamthif cb sent to amthi client */
+ dev->iamthif_current_cb = priv_cb_pos;
+ list_move_tail(&priv_cb_pos->cb_list,
+ &dev->write_waiting_list.mei_cb.cb_list);
+
+ }
+ } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
+ /* buffer is still empty */
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = file_ext->host_client_id;
+ mei_hdr->me_addr = file_ext->me_client_id;
+ mei_hdr->length =
+ (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
+ mei_hdr->msg_complete = 0;
+ mei_hdr->reserved = 0;
+
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ mei_hdr->length + 3) / 4;
+
+ if (!mei_write_message(dev, mei_hdr,
+ (dev->iamthif_msg_buf +
+ dev->iamthif_msg_buf_index),
+ mei_hdr->length)) {
+ file_ext->status = -ENODEV;
+ list_del(&priv_cb_pos->cb_list);
+ } else {
+ dev->iamthif_msg_buf_index += mei_hdr->length;
+ }
+ return -EMSGSIZE;
+ } else {
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+/**
+ * mei_irq_thread_read_handler - bottom half read routine after ISR to
+ * handle the read processing.
+ *
+ * @cmpl_list: An instance of our list structure
+ * @dev: the device structure
+ * @slots: slots to read.
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list,
+ struct mei_device *dev,
+ s32 *slots)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+ int ret = 0;
+
+ if (!dev->rd_msg_hdr) {
+ dev->rd_msg_hdr = mei_mecbrw_read(dev);
+ dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
+ (*slots)--;
+ dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
+ }
+ mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
+ dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length);
+
+ if (mei_hdr->reserved || !dev->rd_msg_hdr) {
+ dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
+ ret = -EBADMSG;
+ goto end;
+ }
+
+ if (mei_hdr->host_addr || mei_hdr->me_addr) {
+ list_for_each_entry_safe(file_pos, file_next,
+ &dev->file_list, link) {
+ dev_dbg(&dev->pdev->dev,
+ "list_for_each_entry_safe read host"
+ " client = %d, ME client = %d\n",
+ file_pos->host_client_id,
+ file_pos->me_client_id);
+ if (file_pos->host_client_id == mei_hdr->host_addr &&
+ file_pos->me_client_id == mei_hdr->me_addr)
+ break;
+ }
+
+ if (&file_pos->link == &dev->file_list) {
+ dev_dbg(&dev->pdev->dev, "corrupted message header\n");
+ ret = -EBADMSG;
+ goto end;
+ }
+ }
+ if (((*slots) * sizeof(u32)) < mei_hdr->length) {
+ dev_dbg(&dev->pdev->dev,
+ "we can't read the message slots =%08x.\n",
+ *slots);
+ /* we can't read the message */
+ ret = -ERANGE;
+ goto end;
+ }
+
+ /* decide where to read the message too */
+ if (!mei_hdr->host_addr) {
+ dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
+ mei_irq_thread_read_bus_message(dev, mei_hdr);
+ dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
+ } else if (mei_hdr->host_addr == dev->iamthif_file_ext.host_client_id &&
+ (MEI_FILE_CONNECTED == dev->iamthif_file_ext.state) &&
+ (dev->iamthif_state == MEI_IAMTHIF_READING)) {
+ dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
+ dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
+ mei_hdr->length);
+ ret = mei_irq_thread_read_amthi_message(cmpl_list,
+ dev, mei_hdr);
+ if (ret)
+ goto end;
+
+ } else {
+ dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
+ ret = mei_irq_thread_read_client_message(cmpl_list,
+ dev, mei_hdr);
+ if (ret)
+ goto end;
+
+ }
+
+ /* reset the number of slots and header */
+ *slots = mei_count_full_read_slots(dev);
+ dev->rd_msg_hdr = 0;
+
+ if (*slots == -EOVERFLOW) {
+ /* overflow - reset */
+ dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
+ /* set the event since message has been read */
+ ret = -ERANGE;
+ goto end;
+ }
+end:
+ return ret;
+}
+
+
+/**
+ * mei_irq_thread_write_handler - bottom half write routine after
+ * ISR to handle the write processing.
+ *
+ * @cmpl_list: An instance of our list structure
+ * @dev: the device structure
+ * @slots: slots to write.
+ *
+ * returns 0 on success, <0 on failure.
+ */
+static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
+ struct mei_device *dev,
+ s32 *slots)
+{
+
+ struct mei_cl *file_ext;
+ struct mei_cl_cb *priv_cb_pos = NULL, *priv_cb_next = NULL;
+ struct mei_io_list *list;
+ int ret;
+
+ if (!mei_host_buffer_is_empty(dev)) {
+ dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
+ return 0;
+ }
+ dev->write_hang = -1;
+ *slots = mei_count_empty_write_slots(dev);
+ /* complete all waiting for write CB */
+ dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
+
+ list = &dev->write_waiting_list;
+ if (!list->status && !list_empty(&list->mei_cb.cb_list)) {
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &list->mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)
+ priv_cb_pos->file_private;
+ if (file_ext) {
+ file_ext->status = 0;
+ list_del(&priv_cb_pos->cb_list);
+ if (MEI_WRITING == file_ext->writing_state &&
+ (priv_cb_pos->major_file_operations ==
+ MEI_WRITE) &&
+ (file_ext != &dev->iamthif_file_ext)) {
+ dev_dbg(&dev->pdev->dev,
+ "MEI WRITE COMPLETE\n");
+ file_ext->writing_state =
+ MEI_WRITE_COMPLETE;
+ list_add_tail(&priv_cb_pos->cb_list,
+ &cmpl_list->mei_cb.cb_list);
+ }
+ if (file_ext == &dev->iamthif_file_ext) {
+ dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
+ if (dev->iamthif_flow_control_pending) {
+ ret =
+ _mei_irq_thread_iamthif_read(
+ dev, slots);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ }
+ }
+
+ if (dev->stop && !dev->wd_pending) {
+ dev->wd_stopped = 1;
+ wake_up_interruptible(&dev->wait_stop_wd);
+ return 0;
+ }
+
+ if (dev->extra_write_index) {
+ dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n",
+ dev->extra_write_index);
+ mei_write_message(dev,
+ (struct mei_msg_hdr *) &dev->ext_msg_buf[0],
+ (unsigned char *) &dev->ext_msg_buf[1],
+ (dev->extra_write_index - 1) * sizeof(u32));
+ *slots -= dev->extra_write_index;
+ dev->extra_write_index = 0;
+ }
+ if (dev->mei_state == MEI_ENABLED) {
+ if (dev->wd_pending && mei_flow_ctrl_creds(dev, &dev->wd_cl)) {
+ if (mei_wd_send(dev))
+ dev_dbg(&dev->pdev->dev, "wd send failed.\n");
+ else
+ mei_flow_ctrl_reduce(dev, &dev->wd_cl);
+
+ dev->wd_pending = 0;
+
+ if (dev->wd_timeout) {
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ MEI_START_WD_DATA_SIZE + 3) / 4;
+ dev->wd_due_counter = 2;
+ } else {
+ *slots -= (sizeof(struct mei_msg_hdr) +
+ MEI_WD_PARAMS_SIZE + 3) / 4;
+ dev->wd_due_counter = 0;
+ }
+
+ }
+ }
+ if (dev->stop)
+ return ~ENODEV;
+
+ /* complete control write list CB */
+ if (!dev->ctrl_wr_list.status) {
+ /* complete control write list CB */
+ dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)
+ priv_cb_pos->file_private;
+ if (!file_ext) {
+ list_del(&priv_cb_pos->cb_list);
+ return -ENODEV;
+ }
+ switch (priv_cb_pos->major_file_operations) {
+ case MEI_CLOSE:
+ /* send disconnect message */
+ ret = _mei_irq_thread_close(dev, slots,
+ priv_cb_pos,
+ file_ext, cmpl_list);
+ if (ret)
+ return ret;
+
+ break;
+ case MEI_READ:
+ /* send flow control message */
+ ret = _mei_irq_thread_read(dev, slots,
+ priv_cb_pos,
+ file_ext, cmpl_list);
+ if (ret)
+ return ret;
+
+ break;
+ case MEI_IOCTL:
+ /* connect message */
+ if (!mei_other_client_is_connecting(dev,
+ file_ext))
+ continue;
+ ret = _mei_irq_thread_ioctl(dev, slots,
+ priv_cb_pos,
+ file_ext, cmpl_list);
+ if (ret)
+ return ret;
+
+ break;
+
+ default:
+ BUG();
+ }
+
+ }
+ }
+ /* complete write list CB */
+ if (!dev->write_list.status &&
+ !list_empty(&dev->write_list.mei_cb.cb_list)) {
+ dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->write_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)
+ priv_cb_pos->file_private;
+
+ if (file_ext) {
+ if (file_ext != &dev->iamthif_file_ext) {
+ if (!mei_flow_ctrl_creds(dev,
+ file_ext)) {
+ dev_dbg(&dev->pdev->dev,
+ "No flow control"
+ " credentials for client"
+ " %d, not sending.\n",
+ file_ext->host_client_id);
+ continue;
+ }
+ ret = _mei_irq_thread_cmpl(dev, slots,
+ priv_cb_pos,
+ file_ext,
+ cmpl_list);
+ if (ret)
+ return ret;
+
+ } else if (file_ext == &dev->iamthif_file_ext) {
+ /* IAMTHIF IOCTL */
+ dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n");
+ if (!mei_flow_ctrl_creds(dev,
+ file_ext)) {
+ dev_dbg(&dev->pdev->dev,
+ "No flow control"
+ " credentials for amthi"
+ " client %d.\n",
+ file_ext->host_client_id);
+ continue;
+ }
+ ret = _mei_irq_thread_cmpl_iamthif(dev,
+ slots,
+ priv_cb_pos,
+ file_ext,
+ cmpl_list);
+ if (ret)
+ return ret;
+
+ }
+ }
+
+ }
+ }
+ return 0;
+}
+
+
+
+/**
+ * mei_timer - timer function.
+ *
+ * @work: pointer to the work_struct structure
+ *
+ * NOTE: This function is called by timer interrupt work
+ */
+void mei_wd_timer(struct work_struct *work)
+{
+ unsigned long timeout;
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+ struct list_head *amthiCompletelist = NULL;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+
+ struct mei_device *dev = container_of(work,
+ struct mei_device, wd_work.work);
+
+
+ mutex_lock(&dev->device_lock);
+ if (dev->mei_state != MEI_ENABLED) {
+ if (dev->mei_state == MEI_INIT_CLIENTS) {
+ if (dev->init_clients_timer) {
+ if (--dev->init_clients_timer == 0) {
+ dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
+ dev->init_clients_state);
+ mei_reset(dev, 1);
+ }
+ }
+ }
+ goto out;
+ }
+ /*** connect/disconnect timeouts ***/
+ list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
+ if (file_pos->timer_count) {
+ if (--file_pos->timer_count == 0) {
+ dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
+ mei_reset(dev, 1);
+ goto out;
+ }
+ }
+ }
+
+ if (dev->wd_cl.state != MEI_FILE_CONNECTED)
+ goto out;
+
+ /* Watchdog */
+ if (dev->wd_due_counter && !dev->wd_bypass) {
+ if (--dev->wd_due_counter == 0) {
+ if (dev->mei_host_buffer_is_empty &&
+ mei_flow_ctrl_creds(dev, &dev->wd_cl)) {
+ dev->mei_host_buffer_is_empty = 0;
+ dev_dbg(&dev->pdev->dev, "send watchdog.\n");
+
+ if (mei_wd_send(dev))
+ dev_dbg(&dev->pdev->dev, "wd send failed.\n");
+ else
+ mei_flow_ctrl_reduce(dev, &dev->wd_cl);
+
+ if (dev->wd_timeout)
+ dev->wd_due_counter = 2;
+ else
+ dev->wd_due_counter = 0;
+
+ } else
+ dev->wd_pending = 1;
+
+ }
+ }
+ if (dev->iamthif_stall_timer) {
+ if (--dev->iamthif_stall_timer == 0) {
+ dev_dbg(&dev->pdev->dev, "reseting because of hang to amthi.\n");
+ mei_reset(dev, 1);
+ dev->iamthif_msg_buf_size = 0;
+ dev->iamthif_msg_buf_index = 0;
+ dev->iamthif_canceled = 0;
+ dev->iamthif_ioctl = 1;
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ dev->iamthif_timer = 0;
+
+ if (dev->iamthif_current_cb)
+ mei_free_cb_private(dev->iamthif_current_cb);
+
+ dev->iamthif_file_object = NULL;
+ dev->iamthif_current_cb = NULL;
+ run_next_iamthif_cmd(dev);
+ }
+ }
+
+ if (dev->iamthif_timer) {
+
+ timeout = dev->iamthif_timer +
+ msecs_to_jiffies(IAMTHIF_READ_TIMER);
+
+ dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
+ dev->iamthif_timer);
+ dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
+ dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
+ if (time_after(jiffies, timeout)) {
+ /*
+ * User didn't read the AMTHI data on time (15sec)
+ * freeing AMTHI for other requests
+ */
+
+ dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
+
+ amthiCompletelist = &dev->amthi_read_complete_list.
+ mei_cb.cb_list;
+
+ if (!list_empty(amthiCompletelist)) {
+
+ list_for_each_entry_safe(priv_cb_pos,
+ priv_cb_next,
+ amthiCompletelist,
+ cb_list) {
+
+ file_pos = priv_cb_pos->file_object->private_data;
+
+ /* Finding the AMTHI entry. */
+ if (file_pos == &dev->iamthif_file_ext)
+ list_del(&priv_cb_pos->cb_list);
+ }
+ }
+ if (dev->iamthif_current_cb)
+ mei_free_cb_private(dev->iamthif_current_cb);
+
+ dev->iamthif_file_object->private_data = NULL;
+ dev->iamthif_file_object = NULL;
+ dev->iamthif_current_cb = NULL;
+ dev->iamthif_timer = 0;
+ run_next_iamthif_cmd(dev);
+
+ }
+ }
+out:
+ schedule_delayed_work(&dev->wd_work, 2 * HZ);
+ mutex_unlock(&dev->device_lock);
+}
+
+/**
+ * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
+ * processing.
+ *
+ * @irq: The irq number
+ * @dev_id: pointer to the device structure
+ *
+ * returns irqreturn_t
+ *
+ */
+irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
+{
+ struct mei_device *dev = (struct mei_device *) dev_id;
+ struct mei_io_list complete_list;
+ struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
+ struct mei_cl *file_ext;
+ s32 slots;
+ int rets;
+ bool bus_message_received;
+
+
+ dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
+ /* initialize our complete list */
+ mutex_lock(&dev->device_lock);
+ mei_initialize_list(&complete_list, dev);
+ dev->host_hw_state = mei_hcsr_read(dev);
+ dev->me_hw_state = mei_mecsr_read(dev);
+
+ /* check if ME wants a reset */
+ if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
+ dev->mei_state != MEI_RESETING &&
+ dev->mei_state != MEI_INITIALIZING) {
+ dev_dbg(&dev->pdev->dev, "FW not ready.\n");
+ mei_reset(dev, 1);
+ mutex_unlock(&dev->device_lock);
+ return IRQ_HANDLED;
+ }
+
+ /* check if we need to start the dev */
+ if ((dev->host_hw_state & H_RDY) == 0) {
+ if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
+ dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
+ dev->host_hw_state |= (H_IE | H_IG | H_RDY);
+ mei_hcsr_set(dev);
+ dev->mei_state = MEI_INIT_CLIENTS;
+ dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
+ /* link is established
+ * start sending messages.
+ */
+ host_start_message(dev);
+ mutex_unlock(&dev->device_lock);
+ return IRQ_HANDLED;
+ } else {
+ dev_dbg(&dev->pdev->dev, "FW not ready.\n");
+ mutex_unlock(&dev->device_lock);
+ return IRQ_HANDLED;
+ }
+ }
+ /* check slots avalable for reading */
+ slots = mei_count_full_read_slots(dev);
+ dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
+ slots, dev->extra_write_index);
+ while (slots > 0 && !dev->extra_write_index) {
+ dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
+ slots, dev->extra_write_index);
+ dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
+ rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
+ if (rets)
+ goto end;
+ }
+ rets = mei_irq_thread_write_handler(&complete_list, dev, &slots);
+end:
+ dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
+ dev->host_hw_state = mei_hcsr_read(dev);
+ dev->mei_host_buffer_is_empty = mei_host_buffer_is_empty(dev);
+
+ bus_message_received = false;
+ if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
+ dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
+ bus_message_received = true;
+ }
+ mutex_unlock(&dev->device_lock);
+ if (bus_message_received) {
+ dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
+ wake_up_interruptible(&dev->wait_recvd_msg);
+ bus_message_received = false;
+ }
+ if (complete_list.status || list_empty(&complete_list.mei_cb.cb_list))
+ return IRQ_HANDLED;
+
+
+ list_for_each_entry_safe(cb_pos, cb_next,
+ &complete_list.mei_cb.cb_list, cb_list) {
+ file_ext = (struct mei_cl *)cb_pos->file_private;
+ list_del(&cb_pos->cb_list);
+ if (file_ext) {
+ if (file_ext != &dev->iamthif_file_ext) {
+ dev_dbg(&dev->pdev->dev, "completing call back.\n");
+ _mei_cmpl(file_ext, cb_pos);
+ cb_pos = NULL;
+ } else if (file_ext == &dev->iamthif_file_ext) {
+ _mei_cmpl_iamthif(dev, cb_pos);
+ }
+ }
+ }
+ return IRQ_HANDLED;
+}
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:30:26

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 4/8] staging/mei: MEI driver init flow.

Init driver list and queue, MEI Hardware reset flow,
init of driver specific host client.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/init.c | 769 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 769 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/init.c

diff --git a/drivers/staging/mei/init.c b/drivers/staging/mei/init.c
new file mode 100644
index 0000000..72f70cf
--- /dev/null
+++ b/drivers/staging/mei/init.c
@@ -0,0 +1,769 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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 "hw.h"
+#include "interface.h"
+#include "mei.h"
+
+const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
+ 0xa8, 0x46, 0xe0, 0xff, 0x65,
+ 0x81, 0x4c);
+
+/**
+ * mei_initialize_list - Sets up a queue list.
+ *
+ * @list: An instance of our list structure
+ * @dev: the device structure
+ */
+void mei_initialize_list(struct mei_io_list *list, struct mei_device *dev)
+{
+ /* initialize our queue list */
+ INIT_LIST_HEAD(&list->mei_cb.cb_list);
+ list->status = 0;
+ list->device_extension = dev;
+}
+
+/**
+ * mei_flush_queues - flushes queue lists belonging to file_ext.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ */
+void mei_flush_queues(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ int i;
+
+ if (!dev || !file_ext)
+ return;
+
+ for (i = 0; i < MEI_IO_LISTS_NUMBER; i++) {
+ dev_dbg(&dev->pdev->dev, "remove list entry belonging to file_ext\n");
+ mei_flush_list(dev->io_list_array[i], file_ext);
+ }
+}
+
+
+/**
+ * mei_flush_list - removes list entry belonging to file_ext.
+ *
+ * @list: An instance of our list structure
+ * @file_ext: private data of the file object
+ */
+void mei_flush_list(struct mei_io_list *list, struct mei_cl *file_ext)
+{
+ struct mei_cl *file_ext_tmp;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+
+ if (!list || !file_ext)
+ return;
+
+ if (list->status != 0)
+ return;
+
+ if (list_empty(&list->mei_cb.cb_list))
+ return;
+
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &list->mei_cb.cb_list, cb_list) {
+ if (priv_cb_pos) {
+ file_ext_tmp = (struct mei_cl *)
+ priv_cb_pos->file_private;
+ if (file_ext_tmp &&
+ mei_fe_same_id(file_ext, file_ext_tmp))
+ list_del(&priv_cb_pos->cb_list);
+ }
+ }
+}
+
+/**
+ * mei_reset_iamthif_params - initializes mei device iamthif
+ *
+ * @dev: the device structure
+ */
+static void mei_reset_iamthif_params(struct mei_device *dev)
+{
+ /* reset iamthif parameters. */
+ dev->iamthif_current_cb = NULL;
+ dev->iamthif_msg_buf_size = 0;
+ dev->iamthif_msg_buf_index = 0;
+ dev->iamthif_canceled = 0;
+ dev->iamthif_ioctl = 0;
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ dev->iamthif_timer = 0;
+}
+
+/**
+ * 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.
+ */
+struct mei_device *init_mei_device(struct pci_dev *pdev)
+{
+ int i;
+ struct mei_device *dev;
+
+ dev = kzalloc(sizeof(struct mei_device), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ /* setup our list array */
+ dev->io_list_array[0] = &dev->read_list;
+ dev->io_list_array[1] = &dev->write_list;
+ dev->io_list_array[2] = &dev->write_waiting_list;
+ dev->io_list_array[3] = &dev->ctrl_wr_list;
+ dev->io_list_array[4] = &dev->ctrl_rd_list;
+ dev->io_list_array[5] = &dev->amthi_cmd_list;
+ dev->io_list_array[6] = &dev->amthi_read_complete_list;
+ INIT_LIST_HEAD(&dev->file_list);
+ INIT_LIST_HEAD(&dev->wd_cl.link);
+ INIT_LIST_HEAD(&dev->iamthif_file_ext.link);
+ mutex_init(&dev->device_lock);
+ init_waitqueue_head(&dev->wait_recvd_msg);
+ init_waitqueue_head(&dev->wait_stop_wd);
+ dev->mei_state = MEI_INITIALIZING;
+ dev->iamthif_state = MEI_IAMTHIF_IDLE;
+ for (i = 0; i < MEI_IO_LISTS_NUMBER; i++)
+ mei_initialize_list(dev->io_list_array[i], dev);
+ 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);
+
+ dev->recvd_msg = 0;
+ 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,
+ dev->recvd_msg, MEI_INTEROP_TIMEOUT);
+ mutex_lock(&dev->device_lock);
+ }
+
+ if (!err && !dev->recvd_msg) {
+ dev->mei_state = MEI_DISABLED;
+ 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))) {
+ dev->mei_state = MEI_DISABLED;
+ dev_dbg(&dev->pdev->dev,
+ "host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
+ dev->host_hw_state, dev->me_hw_state);
+
+ if (!(dev->host_hw_state & H_RDY) != H_RDY)
+ dev_dbg(&dev->pdev->dev, "host turn off H_RDY.\n");
+
+ if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
+ dev_dbg(&dev->pdev->dev, "ME turn off ME_RDY.\n");
+
+ printk(KERN_ERR "mei: link layer initialization failed.\n");
+ 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;
+ }
+
+ dev->recvd_msg = 0;
+ 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 *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+ struct mei_cl_cb *priv_cb_pos = NULL;
+ struct mei_cl_cb *priv_cb_next = NULL;
+ bool unexpected;
+
+ if (dev->mei_state == MEI_RECOVERING_FROM_RESET) {
+ dev->need_reset = 1;
+ return;
+ }
+
+ unexpected = (dev->mei_state != MEI_INITIALIZING &&
+ dev->mei_state != MEI_DISABLED &&
+ dev->mei_state != MEI_POWER_DOWN &&
+ dev->mei_state != MEI_POWER_UP);
+
+ 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);
+
+ dev->need_reset = 0;
+
+ if (dev->mei_state != MEI_INITIALIZING) {
+ if (dev->mei_state != MEI_DISABLED &&
+ dev->mei_state != MEI_POWER_DOWN)
+ dev->mei_state = MEI_RESETING;
+
+ list_for_each_entry_safe(file_pos,
+ file_next, &dev->file_list, link) {
+ file_pos->state = MEI_FILE_DISCONNECTED;
+ file_pos->mei_flow_ctrl_creds = 0;
+ file_pos->read_cb = NULL;
+ file_pos->timer_count = 0;
+ }
+ /* remove entry if already in list */
+ dev_dbg(&dev->pdev->dev, "list del iamthif and wd file list.\n");
+ mei_remove_client_from_file_list(dev,
+ dev->wd_cl.host_client_id);
+
+ mei_remove_client_from_file_list(dev,
+ dev->iamthif_file_ext.host_client_id);
+
+ mei_reset_iamthif_params(dev);
+ dev->wd_due_counter = 0;
+ dev->extra_write_index = 0;
+ }
+
+ dev->num_mei_me_clients = 0;
+ dev->rd_msg_hdr = 0;
+ dev->stop = 0;
+ dev->wd_pending = 0;
+
+ /* 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)
+ dev_warn(&dev->pdev->dev, "unexpected reset.\n");
+
+ /* Wake up all readings so they can be interrupted */
+ list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
+ if (waitqueue_active(&file_pos->rx_wait)) {
+ dev_dbg(&dev->pdev->dev, "Waking up client!\n");
+ wake_up_interruptible(&file_pos->rx_wait);
+ }
+ }
+ /* remove all waiting requests */
+ if (dev->write_list.status == 0 &&
+ !list_empty(&dev->write_list.mei_cb.cb_list)) {
+ list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
+ &dev->write_list.mei_cb.cb_list, cb_list) {
+ if (priv_cb_pos) {
+ list_del(&priv_cb_pos->cb_list);
+ mei_free_cb_private(priv_cb_pos);
+ priv_cb_pos = NULL;
+ }
+ }
+ }
+}
+
+
+
+/**
+ * host_start_message - mei host sends start message.
+ *
+ * @dev: the device structure
+ *
+ * returns none.
+ */
+void host_start_message(struct mei_device *dev)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_host_version_request *host_start_req;
+
+ /* host start message */
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_host_version_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ host_start_req =
+ (struct hbm_host_version_request *) &dev->wr_msg_buf[1];
+ memset(host_start_req, 0, sizeof(struct hbm_host_version_request));
+ host_start_req->cmd.cmd = HOST_START_REQ_CMD;
+ host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
+ host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
+ dev->recvd_msg = 0;
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *) (host_start_req),
+ mei_hdr->length)) {
+ dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
+ dev->mei_state = MEI_RESETING;
+ mei_reset(dev, 1);
+ }
+ dev->init_clients_state = MEI_START_MESSAGE;
+ dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
+ return ;
+}
+
+/**
+ * host_enum_clients_message - host sends enumeration client request message.
+ *
+ * @dev: the device structure
+ *
+ * returns none.
+ */
+void host_enum_clients_message(struct mei_device *dev)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_host_enum_request *host_enum_req;
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ /* enumerate clients */
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_host_enum_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ host_enum_req = (struct hbm_host_enum_request *) &dev->wr_msg_buf[1];
+ memset(host_enum_req, 0, sizeof(struct hbm_host_enum_request));
+ host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *) (host_enum_req),
+ mei_hdr->length)) {
+ dev->mei_state = MEI_RESETING;
+ 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;
+ dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
+ return ;
+}
+
+
+/**
+ * allocate_me_clients_storage - allocates storage for me clients
+ *
+ * @dev: the device structure
+ *
+ * returns none.
+ */
+void allocate_me_clients_storage(struct mei_device *dev)
+{
+ 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)
+ dev->num_mei_me_clients++;
+
+ if (dev->num_mei_me_clients <= 0)
+ 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",
+ dev->num_mei_me_clients * sizeof(struct mei_me_client));
+ /* allocate storage for ME clients representation */
+ clients = kcalloc(dev->num_mei_me_clients,
+ sizeof(struct mei_me_client), GFP_KERNEL);
+ if (!clients) {
+ dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
+ dev->mei_state = MEI_RESETING;
+ mei_reset(dev, 1);
+ return ;
+ }
+ dev->me_clients = clients;
+ return ;
+}
+/**
+ * host_client_properties - reads properties for client
+ *
+ * @dev: the device structure
+ *
+ * returns none.
+ */
+void host_client_properties(struct mei_device *dev)
+{
+ struct mei_msg_hdr *mei_header;
+ struct hbm_props_request *host_cli_req;
+ int b;
+ u8 client_num = dev->me_client_presentation_num;
+
+ b = dev->me_client_index;
+ b = find_next_bit(dev->me_clients_map, MEI_CLIENTS_MAX, b);
+ if (b < MEI_CLIENTS_MAX) {
+ dev->me_clients[client_num].client_id = b;
+ dev->me_clients[client_num].mei_flow_ctrl_creds = 0;
+ mei_header = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
+ mei_header->host_addr = 0;
+ mei_header->me_addr = 0;
+ mei_header->length = sizeof(struct hbm_props_request);
+ mei_header->msg_complete = 1;
+ mei_header->reserved = 0;
+
+ host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
+
+ memset(host_cli_req, 0, sizeof(struct hbm_props_request));
+
+ host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
+ host_cli_req->address = b;
+
+ if (!mei_write_message(dev, mei_header,
+ (unsigned char *)host_cli_req,
+ mei_header->length)) {
+ dev->mei_state = MEI_RESETING;
+ dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
+ mei_reset(dev, 1);
+ return;
+ }
+
+ dev->init_clients_timer = INIT_CLIENTS_TIMEOUT;
+ dev->me_client_index = b;
+ return;
+ }
+
+
+ /*
+ * Clear Map for indicating now ME clients
+ * with associated host client
+ */
+ bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
+ dev->write_hang = -1;
+ dev->open_handle_count = 0;
+ bitmap_set(dev->host_clients_map, 0, 3);
+ dev->mei_state = MEI_ENABLED;
+
+ mei_wd_host_init(dev);
+ return;
+}
+
+/**
+ * mei_init_file_private - initializes private file structure.
+ *
+ * @priv: private file structure to be initialized
+ * @file: the file structure
+ */
+void mei_init_file_private(struct mei_cl *priv, struct mei_device *dev)
+{
+ 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;
+}
+
+int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
+{
+ int i, res = -1;
+
+ for (i = 0; i < dev->num_mei_me_clients; ++i)
+ if (uuid_le_cmp(cuuid,
+ dev->me_clients[i].props.protocol_name) == 0) {
+ res = i;
+ break;
+ }
+
+ return res;
+}
+
+
+/**
+ * mei_find_me_client_update_filext - searches for ME client guid
+ * sets client_id in mei_file_private if found
+ * @dev: the device structure
+ * @priv: private file structure to set client_id in
+ * @cguid: searched guid of ME client
+ * @client_id: id of host client to be set in file private structure
+ *
+ * returns ME client index
+ */
+u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
+ const uuid_le *cguid, u8 client_id)
+{
+ int i;
+
+ if (!dev || !priv || !cguid)
+ return 0;
+
+ /* check for valid client id */
+ i = mei_find_me_client_index(dev, *cguid);
+ if (i >= 0) {
+ priv->me_client_id = dev->me_clients[i].client_id;
+ priv->state = MEI_FILE_CONNECTING;
+ priv->host_client_id = client_id;
+
+ list_add_tail(&priv->link, &dev->file_list);
+ return (u8)i;
+ }
+
+ return 0;
+}
+
+/**
+ * host_init_iamthif - mei initialization iamthif client.
+ *
+ * @dev: the device structure
+ *
+ */
+void host_init_iamthif(struct mei_device *dev)
+{
+ u8 i;
+ unsigned char *msg_buf;
+
+ mei_init_file_private(&dev->iamthif_file_ext, dev);
+ dev->iamthif_file_ext.state = MEI_FILE_DISCONNECTED;
+
+ /* find ME amthi client */
+ i = mei_find_me_client_update_filext(dev, &dev->iamthif_file_ext,
+ &mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
+ if (dev->iamthif_file_ext.state != MEI_FILE_CONNECTING) {
+ dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
+ return;
+ }
+
+ /* Do not render the system unusable when iamthif_mtu is not equal to
+ the value received from ME.
+ Assign iamthif_mtu to the value received from ME in order to solve the
+ hardware macro incompatibility. */
+
+ dev_dbg(&dev->pdev->dev, "[DEFAULT] IAMTHIF = %d\n", dev->iamthif_mtu);
+ dev->iamthif_mtu = dev->me_clients[i].props.max_msg_length;
+ dev_dbg(&dev->pdev->dev,
+ "IAMTHIF = %d\n",
+ dev->me_clients[i].props.max_msg_length);
+
+ kfree(dev->iamthif_msg_buf);
+ dev->iamthif_msg_buf = NULL;
+
+ /* allocate storage for ME message buffer */
+ msg_buf = kcalloc(dev->iamthif_mtu,
+ sizeof(unsigned char), GFP_KERNEL);
+ if (!msg_buf) {
+ dev_dbg(&dev->pdev->dev, "memory allocation for ME message buffer failed.\n");
+ return;
+ }
+
+ dev->iamthif_msg_buf = msg_buf;
+
+ if (!mei_connect(dev, &dev->iamthif_file_ext)) {
+ dev_dbg(&dev->pdev->dev, "Failed to connect to AMTHI client\n");
+ dev->iamthif_file_ext.state = MEI_FILE_DISCONNECTED;
+ dev->iamthif_file_ext.host_client_id = 0;
+ } else {
+ dev->iamthif_file_ext.timer_count = CONNECT_TIMEOUT;
+ }
+}
+
+/**
+ * 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
+ */
+struct mei_cl *mei_alloc_file_private(struct mei_device *dev)
+{
+ struct mei_cl *priv;
+
+ priv = kmalloc(sizeof(struct mei_cl), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ mei_init_file_private(priv, dev);
+
+ return priv;
+}
+
+
+
+/**
+ * mei_disconnect_host_client - sends disconnect message to fw from host client.
+ *
+ * @dev: the device structure
+ * @file_ext: 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 *file_ext)
+{
+ int rets, err;
+ long timeout = 15; /* 15 seconds */
+ struct mei_cl_cb *priv_cb;
+
+ if (!dev || !file_ext)
+ return -ENODEV;
+
+ if (file_ext->state != MEI_FILE_DISCONNECTING)
+ return 0;
+
+ priv_cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL);
+ if (!priv_cb)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&priv_cb->cb_list);
+ priv_cb->file_private = file_ext;
+ priv_cb->major_file_operations = MEI_CLOSE;
+ if (dev->mei_host_buffer_is_empty) {
+ dev->mei_host_buffer_is_empty = 0;
+ if (mei_disconnect(dev, file_ext)) {
+ mdelay(10); /* Wait for hardware disconnection ready */
+ list_add_tail(&priv_cb->cb_list,
+ &dev->ctrl_rd_list.mei_cb.cb_list);
+ } else {
+ rets = -ENODEV;
+ dev_dbg(&dev->pdev->dev, "failed to call mei_disconnect.\n");
+ goto free;
+ }
+ } else {
+ dev_dbg(&dev->pdev->dev, "add disconnect cb to control write list\n");
+ list_add_tail(&priv_cb->cb_list,
+ &dev->ctrl_wr_list.mei_cb.cb_list);
+ }
+ mutex_unlock(&dev->device_lock);
+
+ err = wait_event_timeout(dev->wait_recvd_msg,
+ (MEI_FILE_DISCONNECTED == file_ext->state),
+ timeout * HZ);
+
+ mutex_lock(&dev->device_lock);
+ if (MEI_FILE_DISCONNECTED == file_ext->state) {
+ rets = 0;
+ dev_dbg(&dev->pdev->dev, "successfully disconnected from FW client.\n");
+ } else {
+ rets = -ENODEV;
+ if (MEI_FILE_DISCONNECTED != file_ext->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");
+ }
+
+ mei_flush_list(&dev->ctrl_rd_list, file_ext);
+ mei_flush_list(&dev->ctrl_wr_list, file_ext);
+free:
+ mei_free_cb_private(priv_cb);
+ return rets;
+}
+
+/**
+ * mei_remove_client_from_file_list -
+ * removes file private data from device file list
+ *
+ * @dev: the device structure
+ * @host_client_id: host client id to be removed
+ */
+void mei_remove_client_from_file_list(struct mei_device *dev,
+ u8 host_client_id)
+{
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+ list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
+ if (host_client_id == file_pos->host_client_id) {
+ dev_dbg(&dev->pdev->dev, "remove host client = %d, ME client = %d\n",
+ file_pos->host_client_id,
+ file_pos->me_client_id);
+ list_del_init(&file_pos->link);
+ break;
+ }
+ }
+}
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:30:32

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 7/8] staging/mei: AMT Watchdog

code that open connection and invoke
heartbeats to the AMT Watchdog client/feature, if exists

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/wd.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 171 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/wd.c

diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c
new file mode 100644
index 0000000..28db50e
--- /dev/null
+++ b/drivers/staging/mei/wd.c
@@ -0,0 +1,171 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+
+#include "hw.h"
+#include "interface.h"
+#include "mei.h"
+
+/*
+ * MEI Watchdog Module Parameters
+ */
+static u16 watchdog_timeout = AMT_WD_VALUE;
+module_param(watchdog_timeout, ushort, 0);
+MODULE_PARM_DESC(watchdog_timeout,
+ "Intel(R) AMT Watchdog timeout value in seconds. (default="
+ __MODULE_STRING(AMT_WD_VALUE)
+ ", disable=0)");
+
+static const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
+static const u8 mei_stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
+
+const u8 mei_wd_state_independence_msg[3][4] = {
+ {0x05, 0x02, 0x51, 0x10},
+ {0x05, 0x02, 0x52, 0x10},
+ {0x07, 0x02, 0x01, 0x10}
+};
+
+/* UUIDs for AMT F/W clients */
+const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
+ 0x9D, 0xA9, 0x15, 0x14, 0xCB,
+ 0x32, 0xAB);
+
+
+void mei_wd_start_setup(struct mei_device *dev)
+{
+ dev_dbg(&dev->pdev->dev, "dev->wd_timeout=%d.\n", dev->wd_timeout);
+ memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE);
+ memcpy(dev->wd_data + MEI_WD_PARAMS_SIZE,
+ &dev->wd_timeout, sizeof(u16));
+}
+
+/**
+ * host_init_wd - mei initialization wd.
+ *
+ * @dev: the device structure
+ */
+void mei_wd_host_init(struct mei_device *dev)
+{
+ mei_init_file_private(&dev->wd_cl, dev);
+
+ /* look for WD client and connect to it */
+ dev->wd_cl.state = MEI_FILE_DISCONNECTED;
+ dev->wd_timeout = watchdog_timeout;
+
+ if (dev->wd_timeout > 0) {
+ mei_wd_start_setup(dev);
+ /* find ME WD client */
+ mei_find_me_client_update_filext(dev, &dev->wd_cl,
+ &mei_wd_guid, MEI_WD_HOST_CLIENT_ID);
+
+ dev_dbg(&dev->pdev->dev, "check wd_cl\n");
+ if (MEI_FILE_CONNECTING == dev->wd_cl.state) {
+ if (!mei_connect(dev, &dev->wd_cl)) {
+ dev_dbg(&dev->pdev->dev, "Failed to connect to WD client\n");
+ dev->wd_cl.state = MEI_FILE_DISCONNECTED;
+ dev->wd_cl.host_client_id = 0;
+ host_init_iamthif(dev) ;
+ } else {
+ dev->wd_cl.timer_count = CONNECT_TIMEOUT;
+ }
+ } else {
+ dev_dbg(&dev->pdev->dev, "Failed to find WD client\n");
+ host_init_iamthif(dev) ;
+ }
+ } else {
+ dev->wd_bypass = true;
+ dev_dbg(&dev->pdev->dev, "WD requested to be disabled\n");
+ host_init_iamthif(dev) ;
+ }
+}
+
+/**
+ * mei_wd_send - sends watch dog message to fw.
+ *
+ * @dev: the device structure
+ *
+ * returns 0 if success, -EIO when message send fails
+ */
+int mei_wd_send(struct mei_device *dev)
+{
+ struct mei_msg_hdr *mei_hdr;
+
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = dev->wd_cl.host_client_id;
+ mei_hdr->me_addr = dev->wd_cl.me_client_id;
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE))
+ mei_hdr->length = MEI_START_WD_DATA_SIZE;
+ else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE))
+ mei_hdr->length = MEI_WD_PARAMS_SIZE;
+ else
+ BUG();
+
+ if (mei_write_message(dev, mei_hdr, dev->wd_data, mei_hdr->length))
+ return 0;
+ return -EIO;
+}
+
+int mei_wd_stop(struct mei_device *dev, bool preserve)
+{
+ int ret;
+ u16 wd_timeout = dev->wd_timeout;
+
+ cancel_delayed_work(&dev->wd_work);
+ if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout)
+ return 0;
+
+ dev->wd_timeout = 0;
+ dev->wd_due_counter = 0;
+ memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE);
+ dev->stop = 1;
+ if (dev->mei_host_buffer_is_empty &&
+ mei_flow_ctrl_creds(dev, &dev->wd_cl)) {
+ dev->mei_host_buffer_is_empty = 0;
+
+ if (mei_wd_send(dev))
+ dev_dbg(&dev->pdev->dev, "send stop WD failed\n");
+ else
+ mei_flow_ctrl_reduce(dev, &dev->wd_cl);
+
+ dev->wd_pending = 0;
+ } else {
+ dev->wd_pending = 1;
+ }
+ dev->wd_stopped = 0;
+ mutex_unlock(&dev->device_lock);
+
+ ret = wait_event_interruptible_timeout(dev->wait_stop_wd,
+ dev->wd_stopped, 10 * HZ);
+ mutex_lock(&dev->device_lock);
+ if (!dev->wd_stopped)
+ dev_dbg(&dev->pdev->dev, "stop wd failed to complete.\n");
+ else
+ dev_dbg(&dev->pdev->dev, "stop wd complete.\n");
+
+ if (preserve)
+ dev->wd_timeout = wd_timeout;
+
+ return ret;
+}
+
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:30:36

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

Changes of Kconfig and Makefile in drivers/staging
for adding the Intel MEI Driver to the Linux kernel.
New Makefile and Kconfig building for MEI Driver.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/Kconfig | 2 ++
drivers/staging/Makefile | 1 +
drivers/staging/mei/Kconfig | 10 ++++++++++
drivers/staging/mei/Makefile | 20 ++++++++++++++++++++
4 files changed, 33 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/Kconfig
create mode 100644 drivers/staging/mei/Makefile

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e3786f1..856c0d6 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -177,5 +177,7 @@ source "drivers/staging/gma500/Kconfig"

source "drivers/staging/altera-stapl/Kconfig"

+source "drivers/staging/mei/Kconfig"
+
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index f0d5c53..73e7f80 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -70,3 +70,4 @@ obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/
obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/
obj-$(CONFIG_DRM_PSB) += gma500/
+obj-$(CONFIG_INTEL_MEI) += mei/
diff --git a/drivers/staging/mei/Kconfig b/drivers/staging/mei/Kconfig
new file mode 100644
index 0000000..5beb2b1
--- /dev/null
+++ b/drivers/staging/mei/Kconfig
@@ -0,0 +1,10 @@
+config INTEL_MEI
+ tristate "Intel Management Engine Interface (Intel MEI)"
+ depends on X86 && EXPERIMENTAL
+ help
+ The Intel Management Engine (Intel ME) provides Manageability,
+ Security and Media services for system containing Intel chipsets.
+ if selected /dev/mei misc device will be created.
+
+ For more information see
+ <http://software.intel.com/en-us/manageability/>
diff --git a/drivers/staging/mei/Makefile b/drivers/staging/mei/Makefile
new file mode 100644
index 0000000..21a5088
--- /dev/null
+++ b/drivers/staging/mei/Makefile
@@ -0,0 +1,20 @@
+#
+# Makefile - Intel Management Engine Interface (Intel MEI) Linux driver
+# Copyright (c) 2010-2011, 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.
+#
+obj-$(CONFIG_INTEL_MEI) += mei.o
+mei-objs := init.o
+mei-objs += interrupt.o
+mei-objs += interface.o
+mei-objs += iorw.o
+mei-objs += main.o
+mei-objs += wd.o
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:30:58

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
include/linux/mei.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 105 insertions(+), 0 deletions(-)
create mode 100644 include/linux/mei.h

diff --git a/include/linux/mei.h b/include/linux/mei.h
new file mode 100644
index 0000000..6da7c4f
--- /dev/null
+++ b/include/linux/mei.h
@@ -0,0 +1,105 @@
+/*
+
+ Intel Management Engine Interface (Intel MEI) Linux driver
+ Intel MEI Interface Header
+
+ This file is provided under a dual BSD/GPLv2 license. When using or
+ redistributing this file, you may do so under either license.
+
+ GPL LICENSE SUMMARY
+
+ Copyright(c) 2003-2011 Intel Corporation. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that 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.
+
+ Contact Information:
+ Intel Corporation.
+ [email protected]
+ http://www.intel.com
+
+
+ BSD LICENSE
+
+ Copyright(c) 2003-2011 Intel Corporation. All rights reserved.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+
+#ifndef _LINUX_MEI_H
+#define _LINUX_MEI_H
+
+#include <linux/uuid.h>
+
+/*
+ * This IOCTL is used to associate the current file descriptor with a
+ * FW Client (given by UUID). This opens a communication channel
+ * between a host client and a FW client. From this point every read and write
+ * will communicate with the associated FW client.
+ * Only in close() (file_operation release()) the communication between
+ * the clients is disconnected
+ *
+ * The IOCTL argument is a struct with a union the contains
+ * the input parameter and the output parameter for this IOCTL.
+ *
+ * The input parameter is UUID of the FW Client.
+ * The output parameter is the properties of the FW client
+ * (FW protocol version and max message size).
+ *
+ */
+#define IOCTL_MEI_CONNECT_CLIENT \
+ _IOWR('H' , 0x01, struct mei_connect_client_data)
+
+/*
+ * Intel MEI client information struct
+ */
+struct mei_client {
+ __u32 max_msg_length;
+ __u8 protocol_version;
+ __u8 reserved[3];
+};
+
+/*
+ * IOCTL Connect Client Data structure
+ */
+struct mei_connect_client_data {
+ union {
+ uuid_le in_client_uuid;
+ struct mei_client out_client_properties;
+ };
+};
+
+#endif /* _LINUX_MEI_H */
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:31:25

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 5/8] staging/mei: Hardware and MEI driver internal struct definition

define the MEI protocol msg structs and
HW registers, also define the MEI internal status and struct

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/hw.h | 510 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 510 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/hw.h

diff --git a/drivers/staging/mei/hw.h b/drivers/staging/mei/hw.h
new file mode 100644
index 0000000..df29dfb
--- /dev/null
+++ b/drivers/staging/mei/hw.h
@@ -0,0 +1,510 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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.
+ *
+ */
+
+#ifndef _MEI_DATA_STRUCTURES_H_
+#define _MEI_DATA_STRUCTURES_H_
+
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/uuid.h>
+
+#define MEI_FC_MESSAGE_RESERVED_LENGTH 5
+
+/*
+ * Number of queue lists used by this driver
+ */
+#define MEI_IO_LISTS_NUMBER 7
+
+#define MEI_CLIENTS_MAX 255
+
+/*
+ * MEI HW Section
+ */
+
+/* MEI registers */
+/* H_CB_WW - Host Circular Buffer (CB) Write Window register */
+#define H_CB_WW 0
+/* H_CSR - Host Control Status register */
+#define H_CSR 4
+/* ME_CB_RW - ME Circular Buffer Read Window register (read only) */
+#define ME_CB_RW 8
+/* ME_CSR_HA - ME Control Status Host Access register (read only) */
+#define ME_CSR_HA 0xC
+
+
+/* register bits of H_CSR (Host Control Status register) */
+/* Host Circular Buffer Depth - maximum number of 32-bit entries in CB */
+#define H_CBD 0xFF000000
+/* Host Circular Buffer Write Pointer */
+#define H_CBWP 0x00FF0000
+/* Host Circular Buffer Read Pointer */
+#define H_CBRP 0x0000FF00
+/* Host Reset */
+#define H_RST 0x00000010
+/* Host Ready */
+#define H_RDY 0x00000008
+/* Host Interrupt Generate */
+#define H_IG 0x00000004
+/* Host Interrupt Status */
+#define H_IS 0x00000002
+/* Host Interrupt Enable */
+#define H_IE 0x00000001
+
+
+/* register bits of ME_CSR_HA (ME Control Status Host Access register) */
+/* ME CB (Circular Buffer) Depth HRA (Host Read Access) - host read only
+access to ME_CBD */
+#define ME_CBD_HRA 0xFF000000
+/* ME CB Write Pointer HRA - host read only access to ME_CBWP */
+#define ME_CBWP_HRA 0x00FF0000
+/* ME CB Read Pointer HRA - host read only access to ME_CBRP */
+#define ME_CBRP_HRA 0x0000FF00
+/* ME Reset HRA - host read only access to ME_RST */
+#define ME_RST_HRA 0x00000010
+/* ME Ready HRA - host read only access to ME_RDY */
+#define ME_RDY_HRA 0x00000008
+/* ME Interrupt Generate HRA - host read only access to ME_IG */
+#define ME_IG_HRA 0x00000004
+/* ME Interrupt Status HRA - host read only access to ME_IS */
+#define ME_IS_HRA 0x00000002
+/* ME Interrupt Enable HRA - host read only access to ME_IE */
+#define ME_IE_HRA 0x00000001
+
+#define MEI_MINORS_BASE 1
+#define MEI_MINORS_COUNT 1
+
+#define MEI_MINOR_NUMBER 1
+#define MEI_MAX_OPEN_HANDLE_COUNT 253
+
+/*
+ * time to wait for MEI to become ready after init
+ */
+#define MEI_INTEROP_TIMEOUT (HZ * 7)
+
+/*
+ * watch dog definition
+ */
+#define MEI_WATCHDOG_DATA_SIZE 16
+#define MEI_START_WD_DATA_SIZE 20
+#define MEI_WD_PARAMS_SIZE 4
+#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0)
+
+#define MEI_WD_HOST_CLIENT_ID 1
+#define MEI_IAMTHIF_HOST_CLIENT_ID 2
+
+
+/* File state */
+enum file_state {
+ MEI_FILE_INITIALIZING = 0,
+ MEI_FILE_CONNECTING,
+ MEI_FILE_CONNECTED,
+ MEI_FILE_DISCONNECTING,
+ MEI_FILE_DISCONNECTED
+};
+
+/* MEI device states */
+enum mei_states {
+ MEI_INITIALIZING = 0,
+ MEI_INIT_CLIENTS,
+ MEI_ENABLED,
+ MEI_RESETING,
+ MEI_DISABLED,
+ MEI_RECOVERING_FROM_RESET,
+ MEI_POWER_DOWN,
+ MEI_POWER_UP
+};
+
+/* init clients states*/
+enum mei_init_clients_states {
+ MEI_START_MESSAGE = 0,
+ MEI_ENUM_CLIENTS_MESSAGE,
+ MEI_CLIENT_PROPERTIES_MESSAGE
+};
+
+enum iamthif_states {
+ MEI_IAMTHIF_IDLE,
+ MEI_IAMTHIF_WRITING,
+ MEI_IAMTHIF_FLOW_CONTROL,
+ MEI_IAMTHIF_READING,
+ MEI_IAMTHIF_READ_COMPLETE
+};
+
+enum mei_file_transaction_states {
+ MEI_IDLE,
+ MEI_WRITING,
+ MEI_WRITE_COMPLETE,
+ MEI_FLOW_CONTROL,
+ MEI_READING,
+ MEI_READ_COMPLETE
+};
+
+/* MEI CB */
+enum mei_cb_major_types {
+ MEI_READ = 0,
+ MEI_WRITE,
+ MEI_IOCTL,
+ MEI_OPEN,
+ MEI_CLOSE
+};
+
+/*
+ * Intel MEI user data struct
+ */
+struct mei_message_data {
+ u32 size;
+ char *data;
+} __packed;
+
+
+
+#define MEI_CONNECT_TIMEOUT 3 /* at least 2 seconds */
+#define IAMTHIF_STALL_TIMER 12 /* seconds */
+#define IAMTHIF_READ_TIMER 10000 /* ms */
+
+struct mei_cl_cb {
+ struct list_head cb_list;
+ enum mei_cb_major_types major_file_operations;
+ void *file_private;
+ struct mei_message_data request_buffer;
+ struct mei_message_data response_buffer;
+ unsigned long information;
+ unsigned long read_time;
+ struct file *file_object;
+};
+
+/* MEI client instance carried as file->pirvate_data*/
+struct mei_cl {
+ struct list_head link;
+ struct mei_device *dev;
+ enum file_state state;
+ wait_queue_head_t tx_wait;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t wait;
+ int read_pending;
+ int status;
+ /* ID of client connected */
+ u8 host_client_id;
+ u8 me_client_id;
+ u8 mei_flow_ctrl_creds;
+ u8 timer_count;
+ enum mei_file_transaction_states reading_state;
+ enum mei_file_transaction_states writing_state;
+ int sm_state;
+ struct mei_cl_cb *read_cb;
+};
+
+struct mei_io_list {
+ struct mei_cl_cb mei_cb;
+ int status;
+ struct mei_device *device_extension;
+};
+
+/*
+ * MEI BUS Interface Section
+ */
+struct mei_msg_hdr {
+ u32 me_addr:8;
+ u32 host_addr:8;
+ u32 length:9;
+ u32 reserved:6;
+ u32 msg_complete:1;
+} __packed;
+
+
+struct hbm_cmd {
+ u8 cmd:7;
+ u8 is_response:1;
+} __packed;
+
+
+struct mei_bus_message {
+ struct hbm_cmd cmd;
+ u8 command_specific_data[];
+} __packed;
+
+struct hbm_version {
+ u8 minor_version;
+ u8 major_version;
+} __packed;
+
+struct hbm_host_version_request {
+ struct hbm_cmd cmd;
+ u8 reserved;
+ struct hbm_version host_version;
+} __packed;
+
+struct hbm_host_version_response {
+ struct hbm_cmd cmd;
+ int host_version_supported;
+ struct hbm_version me_max_version;
+} __packed;
+
+struct hbm_host_stop_request {
+ struct hbm_cmd cmd;
+ u8 reason;
+ u8 reserved[2];
+} __packed;
+
+struct hbm_host_stop_response {
+ struct hbm_cmd cmd;
+ u8 reserved[3];
+} __packed;
+
+struct hbm_me_stop_request {
+ struct hbm_cmd cmd;
+ u8 reason;
+ u8 reserved[2];
+} __packed;
+
+struct hbm_host_enum_request {
+ struct hbm_cmd cmd;
+ u8 reserved[3];
+} __packed;
+
+struct hbm_host_enum_response {
+ struct hbm_cmd cmd;
+ u8 reserved[3];
+ u8 valid_addresses[32];
+} __packed;
+
+struct mei_client_properties {
+ uuid_le protocol_name;
+ u8 protocol_version;
+ u8 max_number_of_connections;
+ u8 fixed_address;
+ u8 single_recv_buf;
+ u32 max_msg_length;
+} __packed;
+
+struct hbm_props_request {
+ struct hbm_cmd cmd;
+ u8 address;
+ u8 reserved[2];
+} __packed;
+
+
+struct hbm_props_response {
+ struct hbm_cmd cmd;
+ u8 address;
+ u8 status;
+ u8 reserved[1];
+ struct mei_client_properties client_properties;
+} __packed;
+
+struct hbm_client_connect_request {
+ struct hbm_cmd cmd;
+ u8 me_addr;
+ u8 host_addr;
+ u8 reserved;
+} __packed;
+
+struct hbm_client_connect_response {
+ struct hbm_cmd cmd;
+ u8 me_addr;
+ u8 host_addr;
+ u8 status;
+} __packed;
+
+struct hbm_client_disconnect_request {
+ struct hbm_cmd cmd;
+ u8 me_addr;
+ u8 host_addr;
+ u8 reserved[1];
+} __packed;
+
+struct hbm_flow_control {
+ struct hbm_cmd cmd;
+ u8 me_addr;
+ u8 host_addr;
+ u8 reserved[MEI_FC_MESSAGE_RESERVED_LENGTH];
+} __packed;
+
+struct mei_me_client {
+ struct mei_client_properties props;
+ u8 client_id;
+ u8 mei_flow_ctrl_creds;
+} __packed;
+
+/* MEI private device struct */
+struct mei_device {
+ struct pci_dev *pdev; /* pointer to pci device struct */
+ /*
+ * lists of queues
+ */
+ /* array of pointers to aio lists */
+ struct mei_io_list *io_list_array[MEI_IO_LISTS_NUMBER];
+ struct mei_io_list read_list; /* driver read queue */
+ struct mei_io_list write_list; /* driver write queue */
+ struct mei_io_list write_waiting_list; /* write waiting queue */
+ struct mei_io_list ctrl_wr_list; /* managed write IOCTL list */
+ struct mei_io_list ctrl_rd_list; /* managed read IOCTL list */
+ struct mei_io_list amthi_cmd_list; /* amthi list for cmd waiting */
+
+ /* driver managed amthi list for reading completed amthi cmd data */
+ struct mei_io_list amthi_read_complete_list;
+ /*
+ * list of files
+ */
+ struct list_head file_list;
+ /*
+ * memory of device
+ */
+ unsigned int mem_base;
+ unsigned int mem_length;
+ void __iomem *mem_addr;
+ /*
+ * lock for the device
+ */
+ struct mutex device_lock; /* device lock */
+ int recvd_msg;
+ struct delayed_work wd_work; /* watch dog deleye work */
+ /*
+ * hw states of host and fw(ME)
+ */
+ u32 host_hw_state;
+ u32 me_hw_state;
+ /*
+ * waiting queue for receive message from FW
+ */
+ wait_queue_head_t wait_recvd_msg;
+ wait_queue_head_t wait_stop_wd;
+
+ /*
+ * mei device states
+ */
+ enum mei_states mei_state;
+ enum mei_init_clients_states init_clients_state;
+ u16 init_clients_timer;
+ int stop;
+
+ u32 extra_write_index;
+ u32 rd_msg_buf[128]; /* used for control messages */
+ u32 wr_msg_buf[128]; /* used for control messages */
+ u32 ext_msg_buf[8]; /* for control responses */
+ u32 rd_msg_hdr;
+
+ struct hbm_version version;
+
+ int mei_host_buffer_is_empty;
+ struct mei_cl wd_cl;
+ struct mei_me_client *me_clients; /* Note: memory has to be allocated */
+ DECLARE_BITMAP(me_clients_map, MEI_CLIENTS_MAX);
+ DECLARE_BITMAP(host_clients_map, MEI_CLIENTS_MAX);
+ u8 num_mei_me_clients;
+ u8 me_client_presentation_num;
+ u8 me_client_index;
+
+ int wd_pending;
+ int wd_stopped;
+ u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */
+ unsigned char wd_data[MEI_START_WD_DATA_SIZE];
+
+
+ u16 wd_due_counter;
+ bool wd_bypass; /* if false, don't refresh watchdog ME client */
+
+ struct file *iamthif_file_object;
+ struct mei_cl iamthif_file_ext;
+ int iamthif_ioctl;
+ int iamthif_canceled;
+ int iamthif_mtu;
+ unsigned long iamthif_timer;
+ u32 iamthif_stall_timer;
+ unsigned char *iamthif_msg_buf; /* Note: memory has to be allocated */
+ u32 iamthif_msg_buf_size;
+ u32 iamthif_msg_buf_index;
+ int iamthif_flow_control_pending;
+ enum iamthif_states iamthif_state;
+ struct mei_cl_cb *iamthif_current_cb;
+ u8 write_hang;
+ int need_reset;
+ long open_handle_count;
+
+};
+
+/**
+ * mei_reg_read - Reads 32bit data from the mei device
+ *
+ * @dev: the device structure
+ * @offset: offset from which to read the data
+ *
+ * returns the byte read.
+ */
+static inline u32 mei_reg_read(struct mei_device *dev,
+ unsigned long offset)
+{
+ return ioread32(dev->mem_addr + offset);
+}
+
+/**
+ * mei_reg_write - Writes 32bit data to the mei device
+ *
+ * @dev: the device structure
+ * @offset: offset from which to write the data
+ * @value: the byte to write
+ */
+static inline void mei_reg_write(struct mei_device *dev,
+ unsigned long offset, u32 value)
+{
+ iowrite32(value, dev->mem_addr + offset);
+}
+
+/**
+ * mei_hcsr_read - Reads 32bit data from the host CSR
+ *
+ * @dev: the device structure
+ *
+ * returns the byte read.
+ */
+static inline u32 mei_hcsr_read(struct mei_device *dev)
+{
+ return mei_reg_read(dev, H_CSR);
+}
+
+/**
+ * mei_mecsr_read - Reads 32bit data from the ME CSR
+ *
+ * @dev: the device structure
+ *
+ * returns ME_CSR_HA register value (u32)
+ */
+static inline u32 mei_mecsr_read(struct mei_device *dev)
+{
+ return mei_reg_read(dev, ME_CSR_HA);
+}
+
+/**
+ * get_me_cb_rw - Reads 32bit data from the mei ME_CB_RW register
+ *
+ * @dev: the device structure
+ *
+ * returns ME_CB_RW register value (u32)
+ */
+static inline u32 mei_mecbrw_read(struct mei_device *dev)
+{
+ return mei_reg_read(dev, ME_CB_RW);
+}
+
+
+/*
+ * mei interface function prototypes
+ */
+void mei_hcsr_set(struct mei_device *dev);
+void mei_csr_clear_his(struct mei_device *dev);
+
+void mei_enable_interrupts(struct mei_device *dev);
+void mei_disable_interrupts(struct mei_device *dev);
+#endif /* _MEI_DATA_STRUCTURES_H_ */
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 10:31:43

by Weil, Oren jer

[permalink] [raw]
Subject: [PATCH 3/8] staging/mei: MEI "Link" layer code - MEI Hardware communications.

functions that handle the communication between clients.
e.g. connect/disconnect to/from a client, send MEI message,
read MEI message, flow control handling.

Signed-off-by: Tomas Winkler <[email protected]>
Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
Signed-off-by: Oren Weil <[email protected]>
---
drivers/staging/mei/interface.c | 440 +++++++++++++++++++++++++++++++++++++++
drivers/staging/mei/interface.h | 124 +++++++++++
2 files changed, 564 insertions(+), 0 deletions(-)
create mode 100644 drivers/staging/mei/interface.c
create mode 100644 drivers/staging/mei/interface.h

diff --git a/drivers/staging/mei/interface.c b/drivers/staging/mei/interface.c
new file mode 100644
index 0000000..620770b
--- /dev/null
+++ b/drivers/staging/mei/interface.c
@@ -0,0 +1,440 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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 "mei.h"
+#include "interface.h"
+
+
+
+/**
+ * mei_set_csr_register - writes H_CSR register to the mei device,
+ * and ignores the H_IS bit for it is write-one-to-zero.
+ *
+ * @dev: the device structure
+ */
+void mei_hcsr_set(struct mei_device *dev)
+{
+ if ((dev->host_hw_state & H_IS) == H_IS)
+ dev->host_hw_state &= ~H_IS;
+ mei_reg_write(dev, H_CSR, dev->host_hw_state);
+ dev->host_hw_state = mei_hcsr_read(dev);
+}
+
+/**
+ * mei_csr_enable_interrupts - enables mei device interrupts
+ *
+ * @dev: the device structure
+ */
+void mei_enable_interrupts(struct mei_device *dev)
+{
+ dev->host_hw_state |= H_IE;
+ mei_hcsr_set(dev);
+}
+
+/**
+ * mei_csr_disable_interrupts - disables mei device interrupts
+ *
+ * @dev: the device structure
+ */
+void mei_disable_interrupts(struct mei_device *dev)
+{
+ dev->host_hw_state &= ~H_IE;
+ mei_hcsr_set(dev);
+}
+
+/**
+ * _host_get_filled_slots - gets number of device filled buffer slots
+ *
+ * @device: the device structure
+ *
+ * returns number of filled slots
+ */
+static unsigned char _host_get_filled_slots(const struct mei_device *dev)
+{
+ char read_ptr, write_ptr;
+
+ read_ptr = (char) ((dev->host_hw_state & H_CBRP) >> 8);
+ write_ptr = (char) ((dev->host_hw_state & H_CBWP) >> 16);
+
+ return (unsigned char) (write_ptr - read_ptr);
+}
+
+/**
+ * mei_host_buffer_is_empty - checks if host buffer is empty.
+ *
+ * @dev: the device structure
+ *
+ * returns 1 if empty, 0 - otherwise.
+ */
+int mei_host_buffer_is_empty(struct mei_device *dev)
+{
+ unsigned char filled_slots;
+
+ dev->host_hw_state = mei_hcsr_read(dev);
+ filled_slots = _host_get_filled_slots(dev);
+
+ if (filled_slots == 0)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * mei_count_empty_write_slots - counts write empty slots.
+ *
+ * @dev: the device structure
+ *
+ * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise empty slots count
+ */
+int mei_count_empty_write_slots(struct mei_device *dev)
+{
+ unsigned char buffer_depth, filled_slots, empty_slots;
+
+ dev->host_hw_state = mei_hcsr_read(dev);
+ buffer_depth = (unsigned char) ((dev->host_hw_state & H_CBD) >> 24);
+ filled_slots = _host_get_filled_slots(dev);
+ empty_slots = buffer_depth - filled_slots;
+
+ /* check for overflow */
+ if (filled_slots > buffer_depth)
+ return -EOVERFLOW;
+
+ return empty_slots;
+}
+
+/**
+ * mei_write_message - writes a message to mei device.
+ *
+ * @dev: the device structure
+ * @header: header of message
+ * @write_buffer: message buffer will be written
+ * @write_length: message size will be written
+ *
+ * returns 1 if success, 0 - otherwise.
+ */
+int mei_write_message(struct mei_device *dev,
+ struct mei_msg_hdr *header,
+ unsigned char *write_buffer,
+ unsigned long write_length)
+{
+ u32 temp_msg = 0;
+ unsigned long bytes_written = 0;
+ unsigned char buffer_depth, filled_slots, empty_slots;
+ unsigned long dw_to_write;
+
+ dev->host_hw_state = mei_hcsr_read(dev);
+
+ dev_dbg(&dev->pdev->dev,
+ "host_hw_state = 0x%08x.\n",
+ dev->host_hw_state);
+
+ dev_dbg(&dev->pdev->dev,
+ "mei_write_message header=%08x.\n",
+ *((u32 *) header));
+
+ buffer_depth = (unsigned char) ((dev->host_hw_state & H_CBD) >> 24);
+ filled_slots = _host_get_filled_slots(dev);
+ empty_slots = buffer_depth - filled_slots;
+ dev_dbg(&dev->pdev->dev,
+ "filled = %hu, empty = %hu.\n",
+ filled_slots, empty_slots);
+
+ dw_to_write = ((write_length + 3) / 4);
+
+ if (dw_to_write > empty_slots)
+ return 0;
+
+ mei_reg_write(dev, H_CB_WW, *((u32 *) header));
+
+ while (write_length >= 4) {
+ mei_reg_write(dev, H_CB_WW,
+ *(u32 *) (write_buffer + bytes_written));
+ bytes_written += 4;
+ write_length -= 4;
+ }
+
+ if (write_length > 0) {
+ memcpy(&temp_msg, &write_buffer[bytes_written], write_length);
+ mei_reg_write(dev, H_CB_WW, temp_msg);
+ }
+
+ dev->host_hw_state |= H_IG;
+ mei_hcsr_set(dev);
+ dev->me_hw_state = mei_mecsr_read(dev);
+ if ((dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
+ return 0;
+
+ dev->write_hang = 0;
+ return 1;
+}
+
+/**
+ * mei_count_full_read_slots - counts read full slots.
+ *
+ * @dev: the device structure
+ *
+ * returns -1(ESLOTS_OVERFLOW) if overflow, otherwise filled slots count
+ */
+int mei_count_full_read_slots(struct mei_device *dev)
+{
+ char read_ptr, write_ptr;
+ unsigned char buffer_depth, filled_slots;
+
+ dev->me_hw_state = mei_mecsr_read(dev);
+ buffer_depth = (unsigned char)((dev->me_hw_state & ME_CBD_HRA) >> 24);
+ read_ptr = (char) ((dev->me_hw_state & ME_CBRP_HRA) >> 8);
+ write_ptr = (char) ((dev->me_hw_state & ME_CBWP_HRA) >> 16);
+ filled_slots = (unsigned char) (write_ptr - read_ptr);
+
+ /* check for overflow */
+ if (filled_slots > buffer_depth)
+ return -EOVERFLOW;
+
+ dev_dbg(&dev->pdev->dev, "filled_slots =%08x\n", filled_slots);
+ return (int)filled_slots;
+}
+
+/**
+ * mei_read_slots - reads a message from mei device.
+ *
+ * @dev: the device structure
+ * @buffer: message buffer will be written
+ * @buffer_length: message size will be read
+ */
+void mei_read_slots(struct mei_device *dev,
+ unsigned char *buffer, unsigned long buffer_length)
+{
+ u32 i = 0;
+ unsigned char temp_buf[sizeof(u32)];
+
+ while (buffer_length >= sizeof(u32)) {
+ ((u32 *) buffer)[i] = mei_mecbrw_read(dev);
+
+ dev_dbg(&dev->pdev->dev,
+ "buffer[%d]= %d\n",
+ i, ((u32 *) buffer)[i]);
+
+ i++;
+ buffer_length -= sizeof(u32);
+ }
+
+ if (buffer_length > 0) {
+ *((u32 *) &temp_buf) = mei_mecbrw_read(dev);
+ memcpy(&buffer[i * 4], temp_buf, buffer_length);
+ }
+
+ dev->host_hw_state |= H_IG;
+ mei_hcsr_set(dev);
+}
+
+/**
+ * mei_flow_ctrl_creds - checks flow_control credentials.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ *
+ * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
+ */
+int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ int i;
+
+ if (!dev->num_mei_me_clients)
+ return 0;
+
+ if (file_ext == NULL)
+ return 0;
+
+ if (file_ext->mei_flow_ctrl_creds > 0)
+ return 1;
+
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id == file_ext->me_client_id) {
+ if (dev->me_clients[i].mei_flow_ctrl_creds > 0) {
+ BUG_ON(dev->me_clients[i].props.single_recv_buf
+ == 0);
+ return 1;
+ }
+ return 0;
+ }
+ }
+ BUG();
+ return 0;
+}
+
+/**
+ * mei_flow_ctrl_reduce - reduces flow_control.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ */
+void mei_flow_ctrl_reduce(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ int i;
+
+ if (!dev->num_mei_me_clients)
+ return;
+
+ for (i = 0; i < dev->num_mei_me_clients; i++) {
+ if (dev->me_clients[i].client_id == file_ext->me_client_id) {
+ if (dev->me_clients[i].props.single_recv_buf != 0) {
+ BUG_ON(dev->me_clients[i].mei_flow_ctrl_creds
+ <= 0);
+ dev->me_clients[i].mei_flow_ctrl_creds--;
+ } else {
+ BUG_ON(file_ext->mei_flow_ctrl_creds <= 0);
+ file_ext->mei_flow_ctrl_creds--;
+ }
+ return;
+ }
+ }
+ BUG();
+}
+
+/**
+ * mei_send_flow_control - sends flow control to fw.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ *
+ * returns 1 if success, 0 - otherwise.
+ */
+int mei_send_flow_control(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_flow_control *mei_flow_control;
+
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_flow_control);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ mei_flow_control = (struct hbm_flow_control *) &dev->wr_msg_buf[1];
+ memset(mei_flow_control, 0, sizeof(mei_flow_control));
+ mei_flow_control->host_addr = file_ext->host_client_id;
+ mei_flow_control->me_addr = file_ext->me_client_id;
+ mei_flow_control->cmd.cmd = MEI_FLOW_CONTROL_CMD;
+ memset(mei_flow_control->reserved, 0,
+ sizeof(mei_flow_control->reserved));
+ dev_dbg(&dev->pdev->dev, "sending flow control host client = %d, ME client = %d\n",
+ file_ext->host_client_id, file_ext->me_client_id);
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *) mei_flow_control,
+ sizeof(struct hbm_flow_control)))
+ return 0;
+
+ return 1;
+
+}
+
+/**
+ * mei_other_client_is_connecting - checks if other
+ * client with the same client id is connected.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ *
+ * returns 1 if other client is connected, 0 - otherwise.
+ */
+int mei_other_client_is_connecting(struct mei_device *dev,
+ struct mei_cl *file_ext)
+{
+ struct mei_cl *file_pos = NULL;
+ struct mei_cl *file_next = NULL;
+
+ list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link) {
+ if ((file_pos->state == MEI_FILE_CONNECTING) &&
+ (file_pos != file_ext) &&
+ file_ext->me_client_id == file_pos->me_client_id)
+ return 1;
+
+ }
+ return 0;
+}
+
+/**
+ * mei_disconnect - sends disconnect message to fw.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ *
+ * returns 1 if success, 0 - otherwise.
+ */
+int mei_disconnect(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_client_disconnect_request *mei_cli_disconnect;
+
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_client_disconnect_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ mei_cli_disconnect =
+ (struct hbm_client_disconnect_request *) &dev->wr_msg_buf[1];
+ memset(mei_cli_disconnect, 0, sizeof(mei_cli_disconnect));
+ mei_cli_disconnect->host_addr = file_ext->host_client_id;
+ mei_cli_disconnect->me_addr = file_ext->me_client_id;
+ mei_cli_disconnect->cmd.cmd = CLIENT_DISCONNECT_REQ_CMD;
+ mei_cli_disconnect->reserved[0] = 0;
+
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *) mei_cli_disconnect,
+ sizeof(struct hbm_client_disconnect_request)))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * mei_connect - sends connect message to fw.
+ *
+ * @dev: the device structure
+ * @file_ext: private data of the file object
+ *
+ * returns 1 if success, 0 - otherwise.
+ */
+int mei_connect(struct mei_device *dev, struct mei_cl *file_ext)
+{
+ struct mei_msg_hdr *mei_hdr;
+ struct hbm_client_connect_request *mei_cli_connect;
+
+ mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
+ mei_hdr->host_addr = 0;
+ mei_hdr->me_addr = 0;
+ mei_hdr->length = sizeof(struct hbm_client_connect_request);
+ mei_hdr->msg_complete = 1;
+ mei_hdr->reserved = 0;
+
+ mei_cli_connect =
+ (struct hbm_client_connect_request *) &dev->wr_msg_buf[1];
+ mei_cli_connect->host_addr = file_ext->host_client_id;
+ mei_cli_connect->me_addr = file_ext->me_client_id;
+ mei_cli_connect->cmd.cmd = CLIENT_CONNECT_REQ_CMD;
+ mei_cli_connect->reserved = 0;
+
+ if (!mei_write_message(dev, mei_hdr,
+ (unsigned char *) mei_cli_connect,
+ sizeof(struct hbm_client_connect_request)))
+ return 0;
+
+ return 1;
+}
diff --git a/drivers/staging/mei/interface.h b/drivers/staging/mei/interface.h
new file mode 100644
index 0000000..1bac32a
--- /dev/null
+++ b/drivers/staging/mei/interface.h
@@ -0,0 +1,124 @@
+/*
+ *
+ * Intel Management Engine Interface (Intel MEI) Linux driver
+ * Copyright (c) 2003-2011, 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.
+ *
+ */
+
+
+
+#ifndef _MEI_INTERFACE_H_
+#define _MEI_INTERFACE_H_
+
+#include <linux/mei.h>
+#include "hw.h"
+
+
+#define HBM_MINOR_VERSION 0
+#define HBM_MAJOR_VERSION 1
+#define HBM_TIMEOUT 1 /* 1 second */
+
+
+#define HOST_START_REQ_CMD 0x01
+#define HOST_START_RES_CMD 0x81
+
+#define HOST_STOP_REQ_CMD 0x02
+#define HOST_STOP_RES_CMD 0x82
+
+#define ME_STOP_REQ_CMD 0x03
+
+#define HOST_ENUM_REQ_CMD 0x04
+#define HOST_ENUM_RES_CMD 0x84
+
+#define HOST_CLIENT_PROPERTIES_REQ_CMD 0x05
+#define HOST_CLIENT_PROPERTIES_RES_CMD 0x85
+
+#define CLIENT_CONNECT_REQ_CMD 0x06
+#define CLIENT_CONNECT_RES_CMD 0x86
+
+#define CLIENT_DISCONNECT_REQ_CMD 0x07
+#define CLIENT_DISCONNECT_RES_CMD 0x87
+
+#define MEI_FLOW_CONTROL_CMD 0x08
+
+
+#define AMT_WD_VALUE 120 /* seconds */
+
+#define MEI_WATCHDOG_DATA_SIZE 16
+#define MEI_START_WD_DATA_SIZE 20
+#define MEI_WD_PARAMS_SIZE 4
+
+
+enum mei_stop_reason_types {
+ DRIVER_STOP_REQUEST = 0x00,
+ DEVICE_D1_ENTRY = 0x01,
+ DEVICE_D2_ENTRY = 0x02,
+ DEVICE_D3_ENTRY = 0x03,
+ SYSTEM_S1_ENTRY = 0x04,
+ SYSTEM_S2_ENTRY = 0x05,
+ SYSTEM_S3_ENTRY = 0x06,
+ SYSTEM_S4_ENTRY = 0x07,
+ SYSTEM_S5_ENTRY = 0x08
+};
+
+enum me_stop_reason_types {
+ FW_UPDATE = 0x00
+};
+
+enum client_connect_status_types {
+ CCS_SUCCESS = 0x00,
+ CCS_NOT_FOUND = 0x01,
+ CCS_ALREADY_STARTED = 0x02,
+ CCS_OUT_OF_RESOURCES = 0x03,
+ CCS_MESSAGE_SMALL = 0x04
+};
+
+enum client_disconnect_status_types {
+ CDS_SUCCESS = 0x00
+};
+
+
+void mei_read_slots(struct mei_device *dev,
+ unsigned char *buffer, unsigned long buffer_length);
+
+int mei_write_message(struct mei_device *dev,
+ struct mei_msg_hdr *header,
+ unsigned char *write_buffer,
+ unsigned long write_length);
+
+int mei_host_buffer_is_empty(struct mei_device *dev);
+
+int mei_count_full_read_slots(struct mei_device *dev);
+
+int mei_count_empty_write_slots(struct mei_device *dev);
+
+int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *file_ext);
+
+int mei_wd_send(struct mei_device *dev);
+int mei_wd_stop(struct mei_device *dev, bool preserve);
+void mei_wd_host_init(struct mei_device *dev);
+void mei_wd_start_setup(struct mei_device *dev);
+
+void mei_flow_ctrl_reduce(struct mei_device *dev,
+ struct mei_cl *file_ext);
+
+int mei_send_flow_control(struct mei_device *dev,
+ struct mei_cl *file_ext);
+
+int mei_disconnect(struct mei_device *dev,
+ struct mei_cl *file_ext);
+int mei_other_client_is_connecting(struct mei_device *dev,
+ struct mei_cl *file_ext);
+int mei_connect(struct mei_device *dev,
+ struct mei_cl *file_ext);
+
+#endif /* _MEI_INTERFACE_H_ */
--
1.7.1

---------------------------------------------------------------------
Intel Israel (74) Limited

This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.

2011-04-27 12:24:59

by Jiri Slaby

[permalink] [raw]
Subject: Re: [PATCH 1/8] staging/mei: PCI device and char driver support.

On 04/27/2011 12:27 PM, Oren Weil wrote:
> + dev->mem_addr = pci_iomap(pdev, 0, 0);
...
> + iounmap(dev->mem_addr);
...
> + if (dev->mem_addr)
> + iounmap(dev->mem_addr);

Just what I noticed after a quick look: you shall use pci_iounmap for
pci_iomap'ped space.

regards,
--
js

2011-04-27 13:53:08

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 7/8] staging/mei: AMT Watchdog

On Wednesday 27 April 2011, Oren Weil wrote:
>
> code that open connection and invoke
> heartbeats to the AMT Watchdog client/feature, if exists
>
> Signed-off-by: Tomas Winkler <[email protected]>
> Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
> Signed-off-by: Oren Weil <[email protected]>

I still think this should be a regular watchdog driver using
the drivers/watchdog user interface.

Arnd

2011-04-27 15:16:00

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Wed, 27 Apr 2011 13:27:35 +0300 Oren Weil wrote:


> +/*
> + * This IOCTL is used to associate the current file descriptor with a
> + * FW Client (given by UUID). This opens a communication channel
> + * between a host client and a FW client. From this point every read and write
> + * will communicate with the associated FW client.
> + * Only in close() (file_operation release()) the communication between
> + * the clients is disconnected
> + *
> + * The IOCTL argument is a struct with a union the contains
> + * the input parameter and the output parameter for this IOCTL.
> + *
> + * The input parameter is UUID of the FW Client.
> + * The output parameter is the properties of the FW client
> + * (FW protocol version and max message size).
> + *
> + */
> +#define IOCTL_MEI_CONNECT_CLIENT \
> + _IOWR('H' , 0x01, struct mei_connect_client_data)


Please update Documentation/ioctl/ioctl-number.txt.

thanks,
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

2011-04-27 15:17:44

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Wed, Apr 27, 2011 at 01:27:35PM +0300, Oren Weil wrote:
> Signed-off-by: Tomas Winkler <[email protected]>
> Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
> Signed-off-by: Oren Weil <[email protected]>
> ---
> include/linux/mei.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++

No, this needs to stay in the drivers/staging/mei/ directory, no staging
code can be outside of it's directory.

Please fix this and resend.

thanks,

greg k-h

2011-04-27 15:17:49

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 0/8] staging/mei: Intel MEI Driver

On Wed, Apr 27, 2011 at 01:27:29PM +0300, Oren Weil wrote:
> Intel MEI Driver
> =======================
> The Intel Management Engine (Intel ME) is an isolated and
> protected computing resources (Coprocessor) residing inside
> Intel chipsets. The Intel ME provides support for computer/IT
> management features.
> The Feature set depends on Intel chipset SKU.
>
> The Intel Management Engine Interface (Intel MEI, previous known
> as HECI) is interface between the Host and Intel ME.
> This interface is exposed to the host as PCI device.
> The Intel MEI Driver is in charge of the communication channel
> between host application and ME feature.
>
> Each ME feature (ME Client) is addressed by GUID/UUID
> and each feature defines its own protocol.
> The protocol is message based with header and payload up to
> 512 bytes.
>
> The driver exposes character device called /dev/mei.
>
> Application maintain communication with a ME feature while
> /dev/mei is open. The feature binding is preformed by calling
> MEI_CONNECT_CLIENT_IOCTL which pass the desired UUID.
> The number of instances of a ME feature that can be opened
> at the same time depends on the ME feature, but most of the
> features allow only single instance
>
> Intel AMT Host Interface (AMTHI) feature requires multiple
> user application maintaining therefore the MEI driver handle
> it internally by maintaining requests queues for it.
>
> Because some of the ME features can change the system
> configuration, the driver by default allowing only privilege
> user to access it.
>
>
> pseudo code:
> struct mei_connect_client_data data;
> fd = open(MEI_DEVICE);
>
> data.d.in_client_uuid = AMTHI_UUID;
>
> ioctl(fd, IOCTL_MEI_CONNECT_CLIENT, &data);
>
> printf(“Ver=%d, MaxLen=%ld\n”,
> data.d.in_client_uuid.protocol_version,
> data.d.in_client_uuid.max_msg_length);
>
> [...]
>
> write(fd, amthi_req_data, amthi_req_data_len);
>
> [...]
>
> read(fd, &amthi_res_data, amthi_res_data_len);
>
> [...]
>
> Module Parameters
> =================
> watchdog_timeout - in order to change the watchdog timeout setting
> the user can use this module parameter.
> this value set the Intel AMT watchdog timeout interval in seconds,
> the default value is 120sec.
> to disable the watchdog set the value 0.
>
> The AMT watchdog is used for monitoring the OS health.
>
> Note: We are aware that this code is not in its best shape
> we are working to make it better and we will appreciate any feedbacks
> and reviews that you can give to improve it.
>
>
> Oren Weil (8):
> staging/mei: PCI device and char driver support.
> staging/mei: Interrupt handling.
> staging/mei: MEI "Link" layer code - MEI Hardware communications.
> staging/mei: MEI driver init flow.
> staging/mei: Hardware and MEI driver internal struct definition
> staging/mei: Header file contain the Userland API, (IOCTL and its
> struct)
> staging/mei: AMT Watchdog
> staging/mei: Updates to staging/Kconfig ane staging/Makefile and a
> new Makefile and Kconfig
>
> drivers/staging/Kconfig | 2 +
> drivers/staging/Makefile | 1 +
> drivers/staging/mei/Kconfig | 10 +
> drivers/staging/mei/Makefile | 20 +
> drivers/staging/mei/hw.h | 510 ++++++++++++
> drivers/staging/mei/init.c | 769 +++++++++++++++++
> drivers/staging/mei/interface.c | 440 ++++++++++
> drivers/staging/mei/interface.h | 124 +++
> drivers/staging/mei/interrupt.c | 1634 +++++++++++++++++++++++++++++++++++++
> drivers/staging/mei/iorw.c | 595 ++++++++++++++
> drivers/staging/mei/main.c | 1340 ++++++++++++++++++++++++++++++
> drivers/staging/mei/mei.h | 148 ++++
> drivers/staging/mei/mei_version.h | 31 +
> drivers/staging/mei/wd.c | 171 ++++
> include/linux/mei.h | 105 +++

We need a TODO file that lists what is needed to be done to the code to
get it moved out of staging. Please include that in your next patch
series resend.

thanks,

greg k-h

2011-04-27 15:17:51

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 1/8] staging/mei: PCI device and char driver support.

On Wed, Apr 27, 2011 at 01:27:30PM +0300, Oren Weil wrote:
> contains module entries and PCI driver and char device
> definitions (using file_operations, pci_driver structs).

That's a very lame description. Please be more verbous, like you did on
the 0/8 patch, that explains what this code is and how it is accessed.

Same goes for the other patches in this series, you need to explain
things more.

thanks,

greg k-h

2011-04-27 15:18:04

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 3/8] staging/mei: MEI "Link" layer code - MEI Hardware communications.

On Wed, Apr 27, 2011 at 01:27:32PM +0300, Oren Weil wrote:
> +/**
> + * mei_flow_ctrl_creds - checks flow_control credentials.
> + *
> + * @dev: the device structure
> + * @file_ext: private data of the file object
> + *
> + * returns 1 if mei_flow_ctrl_creds >0, 0 - otherwise.
> + */
> +int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *file_ext)
> +{
> + int i;
> +
> + if (!dev->num_mei_me_clients)
> + return 0;
> +
> + if (file_ext == NULL)
> + return 0;
> +
> + if (file_ext->mei_flow_ctrl_creds > 0)
> + return 1;
> +
> + for (i = 0; i < dev->num_mei_me_clients; i++) {
> + if (dev->me_clients[i].client_id == file_ext->me_client_id) {
> + if (dev->me_clients[i].mei_flow_ctrl_creds > 0) {
> + BUG_ON(dev->me_clients[i].props.single_recv_buf
> + == 0);
> + return 1;
> + }
> + return 0;
> + }
> + }
> + BUG();
> + return 0;

What?

You just crashed your device with no chance of recovery. NEVER put a
BUG() or BUG_ON() call in a driver, that is just rude in irresponsible.

Especially with a return 0; after it, that's just trying to outsmart the
compiler warning, which is extra foolish.

This happens in other places in this code as well, please fix.

greg k-h

2011-04-27 15:17:55

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

On Wed, Apr 27, 2011 at 01:27:37PM +0300, Oren Weil wrote:
> --- /dev/null
> +++ b/drivers/staging/mei/Kconfig
> @@ -0,0 +1,10 @@
> +config INTEL_MEI
> + tristate "Intel Management Engine Interface (Intel MEI)"
> + depends on X86 && EXPERIMENTAL

You need a lot more dependencies here, please fix this.

> + help
> + The Intel Management Engine (Intel ME) provides Manageability,
> + Security and Media services for system containing Intel chipsets.
> + if selected /dev/mei misc device will be created.
> +
> + For more information see
> + <http://software.intel.com/en-us/manageability/>
> diff --git a/drivers/staging/mei/Makefile b/drivers/staging/mei/Makefile
> new file mode 100644
> index 0000000..21a5088
> --- /dev/null
> +++ b/drivers/staging/mei/Makefile
> @@ -0,0 +1,20 @@
> +#
> +# Makefile - Intel Management Engine Interface (Intel MEI) Linux driver
> +# Copyright (c) 2010-2011, 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.
> +#

This is not a program, it is a Makefile, this header can be removed.

thanks,

greg k-h

2011-04-27 15:18:00

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 7/8] staging/mei: AMT Watchdog

On Wed, Apr 27, 2011 at 03:53:01PM +0200, Arnd Bergmann wrote:
> On Wednesday 27 April 2011, Oren Weil wrote:
> >
> > code that open connection and invoke
> > heartbeats to the AMT Watchdog client/feature, if exists
> >
> > Signed-off-by: Tomas Winkler <[email protected]>
> > Signed-off-by: Itzhak Tzeel-Krupp <[email protected]>
> > Signed-off-by: Oren Weil <[email protected]>
>
> I still think this should be a regular watchdog driver using
> the drivers/watchdog user interface.

I agree, Oren, if you are going to insist on doing things this way, you
had better describe _why_ you are ignoring the existing kernel
interfaces provided.

thanks,

greg k-h

2011-04-27 15:19:21

by Randy Dunlap

[permalink] [raw]
Subject: Re: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

On Wed, 27 Apr 2011 13:27:37 +0300 Oren Weil wrote:


> diff --git a/drivers/staging/mei/Kconfig b/drivers/staging/mei/Kconfig
> new file mode 100644
> index 0000000..5beb2b1
> --- /dev/null
> +++ b/drivers/staging/mei/Kconfig
> @@ -0,0 +1,10 @@
> +config INTEL_MEI
> + tristate "Intel Management Engine Interface (Intel MEI)"
> + depends on X86 && EXPERIMENTAL
> + help
> + The Intel Management Engine (Intel ME) provides Manageability,
> + Security and Media services for system containing Intel chipsets.

systems

> + if selected /dev/mei misc device will be created.

If selected,

> +
> + For more information see
> + <http://software.intel.com/en-us/manageability/>

help text should be indented with 1 tab + 2 spaces (Chapter 10 of CodingStyle).

---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

2011-04-27 15:32:48

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Wed, Apr 27, 2011 at 08:15:41AM -0700, Randy Dunlap wrote:
> On Wed, 27 Apr 2011 13:27:35 +0300 Oren Weil wrote:
>
>
> > +/*
> > + * This IOCTL is used to associate the current file descriptor with a
> > + * FW Client (given by UUID). This opens a communication channel
> > + * between a host client and a FW client. From this point every read and write
> > + * will communicate with the associated FW client.
> > + * Only in close() (file_operation release()) the communication between
> > + * the clients is disconnected
> > + *
> > + * The IOCTL argument is a struct with a union the contains
> > + * the input parameter and the output parameter for this IOCTL.
> > + *
> > + * The input parameter is UUID of the FW Client.
> > + * The output parameter is the properties of the FW client
> > + * (FW protocol version and max message size).
> > + *
> > + */
> > +#define IOCTL_MEI_CONNECT_CLIENT \
> > + _IOWR('H' , 0x01, struct mei_connect_client_data)
>
>
> Please update Documentation/ioctl/ioctl-number.txt.

Not yet, this code might get that ioctl removed before it gets merged
into the main part of the kernel.

Or at least I can hope :)

thanks,

greg k-h

2011-04-28 00:06:20

by Valdis Klētnieks

[permalink] [raw]
Subject: Re: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

On Wed, 27 Apr 2011 13:27:37 +0300, Oren Weil said:
> Changes of Kconfig and Makefile in drivers/staging
> for adding the Intel MEI Driver to the Linux kernel.
> New Makefile and Kconfig building for MEI Driver.

> +++ b/drivers/staging/mei/Kconfig
> @@ -0,0 +1,10 @@
> +config INTEL_MEI
> + tristate "Intel Management Engine Interface (Intel MEI)"
> + depends on X86 && EXPERIMENTAL
> + help
> + The Intel Management Engine (Intel ME) provides Manageability,
> + Security and Media services for system containing Intel chipsets.
> + if selected /dev/mei misc device will be created.
> +
> + For more information see
> + <http://software.intel.com/en-us/manageability/>

Can this be expanded a bit to give a better hint *which* Intel chipsets support
this? I just spent an honest 10 minutes looking around that website, and am no
clearer on whether the 82801I in my laptop supports this. Having a line like
"server chipsets XY948 and later and ICH10 and later" or similar would be a big
help to all those who hit this when it goes into the tree.


Attachments:
(No filename) (227.00 B)

2011-04-28 05:47:11

by Weil, Oren jer

[permalink] [raw]
Subject: RE: [PATCH 1/8] staging/mei: PCI device and char driver support.

>On Wed, Apr 27, 2011 at 01:27:30PM +0300, Oren Weil wrote:
>> contains module entries and PCI driver and char device definitions
>> (using file_operations, pci_driver structs).
>
>That's a very lame description. Please be more verbous, like you did on
the
>0/8 patch, that explains what this code is and how it is accessed.
>
>Same goes for the other patches in this series, you need to explain things
>more.

No problem, should I got into details in the patch description?


Attachments:
smime.p7s (8.39 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-28 05:52:08

by Weil, Oren jer

[permalink] [raw]
Subject: RE: [PATCH 7/8] staging/mei: AMT Watchdog

>On Wed, Apr 27, 2011 at 03:53:01PM +0200, Arnd Bergmann wrote:
>> I still think this should be a regular watchdog driver using the
>> drivers/watchdog user interface.
>
>I agree, Oren, if you are going to insist on doing things this way, you had
>better describe _why_ you are ignoring the existing kernel interfaces
>provided.

we are not going to ignore the existing interfaces, it is one of our items
in the TODO list.
You can see the we move all of the Watchdog code to a single file so it
would easy to us to take it later to other driver.
Another Item on our TODO list is to expose Kernel function that equivalent
to user interface so kernel clients
can access FW features/clients.

---
Oren Weil - Intel Corporation.
http://www.intel.com





Attachments:
smime.p7s (8.39 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-28 06:29:13

by Weil, Oren jer

[permalink] [raw]
Subject: RE: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)


>From: Greg KH [mailto:[email protected]]
>On Wed, Apr 27, 2011 at 08:15:41AM -0700, Randy Dunlap wrote:
>> Please update Documentation/ioctl/ioctl-number.txt.
>
>Not yet, this code might get that ioctl removed before it gets merged into
>the
>main part of the kernel.
>
>Or at least I can hope :)

We discuss this IOCTL issue with Alan C. and David W. and I didn't see any
other solution for this.
if someone has a good solution for that we are happy to hear it.

the reason that we use IOCTL is that we need to "associate" (Connect) the
current file description to a FW Client/Feature.
(opening a comm channel) .
and in addition there is a security issue that we would like limit the access
to mei driver to system admins.

>From what I read we can't get those requirements with Netlinks, right?
If there other methods for achieving this ?

Thanks

---
Oren Weil - Intel Corporation.
http://www.intel.com








Attachments:
smime.p7s (8.39 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-28 06:42:28

by Weil, Oren jer

[permalink] [raw]
Subject: RE: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

>On Wed, Apr 27, 2011 at 01:27:37PM +0300, Oren Weil wrote:

>> +config INTEL_MEI
>> + tristate "Intel Management Engine Interface (Intel MEI)"
>> + depends on X86 && EXPERIMENTAL
>
>You need a lot more dependencies here, please fix this.

Can you give me an example ?

---
Oren Weil - Intel Corporation.
http://www.intel.com




Attachments:
smime.p7s (8.39 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-28 08:38:15

by Alan

[permalink] [raw]
Subject: Re: [PATCH 8/8] staging/mei: Updates to staging/Kconfig ane staging/Makefile and a new Makefile and Kconfig

On Thu, 28 Apr 2011 09:39:57 +0300
"Weil, Oren jer" <[email protected]> wrote:

> >On Wed, Apr 27, 2011 at 01:27:37PM +0300, Oren Weil wrote:
>
> >> +config INTEL_MEI
> >> + tristate "Intel Management Engine Interface (Intel MEI)"
> >> + depends on X86 && EXPERIMENTAL
> >
> >You need a lot more dependencies here, please fix this.
>
> Can you give me an example ?

PCI ?

2011-04-28 12:50:54

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 7/8] staging/mei: AMT Watchdog

On Thursday 28 April 2011, Weil, Oren jer wrote:
>
> >On Wed, Apr 27, 2011 at 03:53:01PM +0200, Arnd Bergmann wrote:
> >> I still think this should be a regular watchdog driver using the
> >> drivers/watchdog user interface.
> >
> >I agree, Oren, if you are going to insist on doing things this way, you had
> >better describe why you are ignoring the existing kernel interfaces
> >provided.
>
> we are not going to ignore the existing interfaces, it is one of our items
> in the TODO list.
> You can see the we move all of the Watchdog code to a single file so it
> would easy to us to take it later to other driver.
> Another Item on our TODO list is to expose Kernel function that equivalent
> to user interface so kernel clients
> can access FW features/clients.

Ok, fair enough. Please put important information like this into the
changelog next time.

Also, your TODO file seems to be missing form the submission.

Arnd

2011-04-28 13:40:26

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 1/8] staging/mei: PCI device and char driver support.

On Thu, Apr 28, 2011 at 08:47:04AM +0300, Weil, Oren jer wrote:
> >On Wed, Apr 27, 2011 at 01:27:30PM +0300, Oren Weil wrote:
> >> contains module entries and PCI driver and char device definitions
> >> (using file_operations, pci_driver structs).
> >
> >That's a very lame description. Please be more verbous, like you did on
> the
> >0/8 patch, that explains what this code is and how it is accessed.
> >
> >Same goes for the other patches in this series, you need to explain things
> >more.
>
> No problem, should I got into details in the patch description?

Yes.

2011-04-28 13:40:31

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 7/8] staging/mei: AMT Watchdog

On Thu, Apr 28, 2011 at 08:51:41AM +0300, Weil, Oren jer wrote:
> >On Wed, Apr 27, 2011 at 03:53:01PM +0200, Arnd Bergmann wrote:
> >> I still think this should be a regular watchdog driver using the
> >> drivers/watchdog user interface.
> >
> >I agree, Oren, if you are going to insist on doing things this way, you had
> >better describe _why_ you are ignoring the existing kernel interfaces
> >provided.
>
> we are not going to ignore the existing interfaces, it is one of our items
> in the TODO list.

Like I stated earlier, there was no TODO list in your patch, so how
could we know this? :)

> You can see the we move all of the Watchdog code to a single file so it
> would easy to us to take it later to other driver.
> Another Item on our TODO list is to expose Kernel function that equivalent
> to user interface so kernel clients can access FW features/clients.

Nice, but again, how could we know this?

thanks,

greg k-h

2011-04-28 13:40:33

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Thu, Apr 28, 2011 at 09:29:05AM +0300, Weil, Oren jer wrote:
>
> >From: Greg KH [mailto:[email protected]]
> >On Wed, Apr 27, 2011 at 08:15:41AM -0700, Randy Dunlap wrote:
> >> Please update Documentation/ioctl/ioctl-number.txt.
> >
> >Not yet, this code might get that ioctl removed before it gets merged into
> >the
> >main part of the kernel.
> >
> >Or at least I can hope :)
>
> We discuss this IOCTL issue with Alan C. and David W. and I didn't see any
> other solution for this.

Was this discussed in public?

> if someone has a good solution for that we are happy to hear it.

Did you rule out sysfs, configfs, netlink, etc.?

> the reason that we use IOCTL is that we need to "associate" (Connect)
> the current file description to a FW Client/Feature. (opening a comm
> channel) .

As you didn't document this ioctl interface anywhere, saying exactly
what you are trying to do, it makes it a bit hard to review your
existing interface to determine that ioctls are the correct thing for
your device.

Care to document it, or at least describe it in the patches somewhere so
we know it and can properly review it?

> and in addition there is a security issue that we would like limit the access
> to mei driver to system admins.
>
> From what I read we can't get those requirements with Netlinks, right?
> If there other methods for achieving this ?

sysfs, configfs, your own filesystem, etc. There are lots of options.

thanks,

greg k-h

2011-04-28 14:14:44

by Weil, Oren jer

[permalink] [raw]
Subject: RE: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

>> the reason that we use IOCTL is that we need to "associate" (Connect)
>> the current file description to a FW Client/Feature. (opening a comm
>> channel) .
>
>As you didn't document this ioctl interface anywhere, saying exactly what you
>are trying to do, it makes it a bit hard to review your existing interface to
>determine that ioctls are the correct thing for your device.

>Care to document it, or at least describe it in the patches somewhere so we
>know it and can properly review it?

In the header file that I defined the IOCTL, I put a comment that describing
the IOCTL.
Also in patch0 there is description about the use case of this IOCTL.
Tell me what kind of information you think that are missing and I will add it.
If needed I will add more information.

>
>> and in addition there is a security issue that we would like limit the
>> access to mei driver to system admins.
>>
>> From what I read we can't get those requirements with Netlinks, right?
>> If there other methods for achieving this ?
>
>sysfs, configfs, your own filesystem, etc. There are lots of options.

Great, we are willing to hear any suggestions that people.
I'm willing to provide any information about the driver that is needed for the
discussion.
.



---
Oren Weil - Intel Corporation.
http://www.intel.com




Attachments:
smime.p7s (8.39 kB)
(No filename) (359.00 B)
Download all attachments

2011-04-28 14:59:48

by Alan

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

> Was this discussed in public?

Not much yet - but this is a staging driver so there is no defined ABI
and nothing is set in stone.

> > if someone has a good solution for that we are happy to hear it.
>
> Did you rule out sysfs, configfs, netlink, etc.?

Pretty much. You need to create a handle, connect it to something and
perform transactions on it. Some of those must be serialized and they
have both input and output data, so they don't fit sysfs and friends.

It's effectively mostly a message passing system - the closest other
interface is the socket one but it doesn't really fit that and I don't
think Dave wants 'its got a connect sort of op so lets make it a socket
family' to be the device norm !

The identifier for the object is also long so doesn't work for a set of
devices by minor number either.

I think the more useful step is going to be some example user space as
that above all will show how it is intended to work

Alan

2011-04-28 15:26:00

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Thu, Apr 28, 2011 at 05:12:45PM +0300, Weil, Oren jer wrote:
> ---------------------------------------------------------------------
> Intel Israel (74) Limited
>
> This e-mail and any attachments may contain confidential material for
> the sole use of the intended recipient(s). Any review or distribution
> by others is strictly prohibited. If you are not the intended
> recipient, please contact the sender and delete all copies.

This type of footer is not allowed on discussion of Linux kernel related
things that are done in public. Please fix your email client/system to
remove it.

thanks,

greg k-h

2011-04-28 15:40:04

by Woodhouse, David

[permalink] [raw]
Subject: Re: [PATCH 6/8] staging/mei: Header file contain the Userland API, (IOCTL and its struct)

On Thu, 2011-04-28 at 08:04 -0700, Greg KH wrote:
> On Thu, Apr 28, 2011 at 05:12:45PM +0300, Weil, Oren jer wrote:
> > ---------------------------------------------------------------------
> > Intel Israel (74) Limited
> >
> > This e-mail and any attachments may contain confidential material for
> > the sole use of the intended recipient(s). Any review or distribution
> > by others is strictly prohibited. If you are not the intended
> > recipient, please contact the sender and delete all copies.
>
> This type of footer is not allowed on discussion of Linux kernel related
> things that are done in public. Please fix your email client/system to
> remove it.

Oren, I specifically told you to fix that before posting, or to post
from a non-Intel account. Why didn't you?

--
David Woodhouse Open Source Technology Centre
[email protected] Intel Corporation