linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7] char/mei: Intel MEI Driver
@ 2011-03-22 10:51 Oren Weil
  2011-03-22 10:51 ` [PATCH 1/7] char/mei: PCI device and char driver support Oren Weil
                   ` (9 more replies)
  0 siblings, 10 replies; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 3992 bytes --]

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 (7):
  char/mei: PCI device and char driver support.
  char/mei: Interrupt handling.
  char/mei: MEI "Link" layer code - MEI Hardware communications.
  char/mei: MEI driver init flow.
  char/mei: Hardware and MEI driver internal struct definition
  char/mei: Header file contain the Userland API, (IOCTL and its
    struct)
  char/mei: Updates to char/Kconfig ane char/Makefile

 drivers/char/Kconfig           |   12 +-
 drivers/char/Makefile          |    1 +
 drivers/char/mei/Makefile      |   19 +
 drivers/char/mei/hw.h          |  511 +++++++++++++
 drivers/char/mei/init.c        |  834 +++++++++++++++++++++
 drivers/char/mei/interface.c   |  478 ++++++++++++
 drivers/char/mei/interface.h   |  122 +++
 drivers/char/mei/interrupt.c   | 1582 ++++++++++++++++++++++++++++++++++++++++
 drivers/char/mei/iorw.c        |  608 +++++++++++++++
 drivers/char/mei/main.c        | 1442 ++++++++++++++++++++++++++++++++++++
 drivers/char/mei/mei.h         |  156 ++++
 drivers/char/mei/mei_version.h |   31 +
 include/linux/mei.h            |  109 +++
 13 files changed, 5904 insertions(+), 1 deletions(-)
 create mode 100644 drivers/char/mei/Makefile
 create mode 100644 drivers/char/mei/hw.h
 create mode 100644 drivers/char/mei/init.c
 create mode 100644 drivers/char/mei/interface.c
 create mode 100644 drivers/char/mei/interface.h
 create mode 100644 drivers/char/mei/interrupt.c
 create mode 100644 drivers/char/mei/iorw.c
 create mode 100644 drivers/char/mei/main.c
 create mode 100644 drivers/char/mei/mei.h
 create mode 100644 drivers/char/mei/mei_version.h
 create mode 100644 include/linux/mei.h


[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 17:22   ` Arnd Bergmann
  2011-03-22 10:51 ` [PATCH 2/7] char/mei: Interrupt handling Oren Weil
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/mei/iorw.c        |  608 +++++++++++++++++
 drivers/char/mei/main.c        | 1442 ++++++++++++++++++++++++++++++++++++++++
 drivers/char/mei/mei.h         |  156 +++++
 drivers/char/mei/mei_version.h |   31 +
 4 files changed, 2237 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/mei/iorw.c
 create mode 100644 drivers/char/mei/main.c
 create mode 100644 drivers/char/mei/mei.h
 create mode 100644 drivers/char/mei/mei_version.h

diff --git a/drivers/char/mei/iorw.c b/drivers/char/mei/iorw.c
new file mode 100644
index 0000000..cb04e36
--- /dev/null
+++ b/drivers/char/mei/iorw.c
@@ -0,0 +1,608 @@
+/*
+ *
+ * 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
+ * @if_num:  minor number
+ * @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 mei_device *dev, int if_num,
+					struct mei_connect_client_data *data,
+					struct file *file)
+{
+	struct mei_cb_private *priv_cb = NULL;
+	struct mei_client *client;
+	struct mei_file_private *file_ext;
+	struct mei_file_private *file_pos = NULL;
+	struct mei_file_private *file_next = NULL;
+	long timeout = CONNECT_TIMEOUT;
+	int i;
+	int err;
+	int rets;
+
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file)
+		return -ENODEV;
+
+	dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n");
+
+	file_ext = file->private_data;
+
+	/* buffered ioctl cb */
+	priv_cb = kzalloc(sizeof(struct mei_cb_private), 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->d.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->d.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->d.out_client_propeties;
+		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->d.out_client_propeties;
+	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;
+
+		if (priv_cb) {
+			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_cb_private *find_amthi_read_list_entry(
+		struct mei_device *dev,
+		struct file *file)
+{
+	struct mei_file_private *file_ext_temp;
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *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_file_private *)
+					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, int if_num, struct file *file,
+	      char __user *ubuf, size_t length, loff_t *offset)
+{
+	int rets;
+	int wait_ret;
+	struct mei_cb_private *priv_cb = NULL;
+	struct mei_file_private *file_ext = file->private_data;
+	int i;
+	unsigned long timeout;
+
+
+	if (if_num != MEI_MINOR_NUMBER || !dev)
+		return -ENODEV;
+
+	if (!file_ext || file_ext != &dev->iamthif_file_ext)
+		return -ENODEV;
+
+	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) {
+		dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
+		return -ENODEV;
+	}
+
+	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");
+
+
+	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, int if_num,
+		    struct mei_file_private *file_ext)
+{
+	struct mei_cb_private *priv_cb;
+	int rets = 0;
+	int i;
+
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext) {
+		dev_dbg(&dev->pdev->dev, "received wrong function input param.\n");
+		return -ENODEV;
+	}
+
+	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_cb_private), 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_cb_private *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_file_private *file_ext_tmp;
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *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_file_private *)
+					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_cb_private *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/char/mei/main.c b/drivers/char/mei/main.c
new file mode 100644
index 0000000..7887e00
--- /dev/null
+++ b/drivers/char/mei/main.c
@@ -0,0 +1,1442 @@
+/*
+ *
+ * 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);
+	cancel_delayed_work(&dev->wd_work);
+	if (dev->wd_file_ext.state == MEI_FILE_CONNECTED && dev->wd_timeout) {
+		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_file_ext)) {
+			dev->mei_host_buffer_is_empty = 0;
+
+			if (!mei_send_wd(dev))
+				dev_dbg(&pdev->dev, "send stop WD failed\n");
+			else
+				mei_flow_ctrl_reduce(dev, &dev->wd_file_ext);
+
+			dev->wd_pending = 0;
+		} else {
+			dev->wd_pending = 1;
+		}
+		dev->wd_stopped = 0;
+		mutex_unlock(&dev->device_lock);
+
+		wait_event_interruptible_timeout(dev->wait_stop_wd,
+				(dev->wd_stopped), 10 * HZ);
+		mutex_lock(&dev->device_lock);
+		if (!dev->wd_stopped)
+			dev_dbg(&pdev->dev, "stop wd failed to complete.\n");
+		else
+			dev_dbg(&pdev->dev, "stop wd complete.\n");
+
+	}
+
+	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_file_ext.state == MEI_FILE_CONNECTED) {
+		dev->wd_file_ext.state = MEI_FILE_DISCONNECTING;
+		mei_disconnect_host_client(dev, &dev->wd_file_ext);
+	}
+
+	/* 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_file_ext.
+					  host_client_id);
+	mei_remove_client_from_file_list(dev,
+			dev->iamthif_file_ext.host_client_id);
+
+	dev->iamthif_current_cb = NULL;
+	dev->iamthif_file_ext.file = 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_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *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_cb_private *find_read_list_entry(
+		struct mei_device *dev,
+		struct mei_file_private *file_ext)
+{
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *priv_cb_next = NULL;
+	struct mei_file_private *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_file_private *)
+				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_file_private *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(file);
+	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_file_private *file_ext = file->private_data;
+	struct mei_cb_private *priv_cb;
+	struct mei_device *dev;
+	int if_num = iminor(inode);
+	int rets = 0;
+
+	if (!mei_device)
+		return -ENODEV;
+
+	dev = pci_get_drvdata(mei_device);
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
+		return -ENODEV;
+
+	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)
+{
+	int i;
+	int rets;
+	int err;
+	int if_num = iminor(file->f_dentry->d_inode);
+	struct mei_file_private *file_ext = file->private_data;
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *priv_cb = NULL;
+	struct mei_device *dev;
+
+	if (!mei_device)
+		return -ENODEV;
+
+	dev = pci_get_drvdata(mei_device);
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
+		return -ENODEV;
+
+	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, if_num, 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, if_num, file_ext);
+	if (err != 0 && 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_file_private *file_ext = file->private_data;
+	struct mei_cb_private *priv_write_cb = NULL;
+	struct mei_msg_hdr mei_hdr;
+	struct mei_device *dev;
+	unsigned long timeout = 0;
+	int if_num = iminor(file->f_dentry->d_inode);
+	int rets;
+	int i;
+
+	if (!mei_device)
+		return -ENODEV;
+
+	dev = pci_get_drvdata(mei_device);
+
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
+		return -ENODEV;
+
+	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_cb_private), 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 inode *inode;
+	struct mei_file_private *file_ext = file->private_data;
+	int rets;
+	int if_num;
+	struct mei_device *dev;
+
+	/* User Data */
+	struct mei_connect_client_data *connect_data = NULL;
+
+	/* Fix in order to use unlocked_ioctl for suppoer kernel >= 2.6.36 */
+	inode = file->f_dentry->d_inode;
+	if_num = iminor(inode);
+
+	if (!mei_device)
+		return -ENODEV;
+
+	dev = pci_get_drvdata(mei_device);
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
+		return -ENODEV;
+
+	mutex_lock(&dev->device_lock);
+	if (dev->mei_state != MEI_ENABLED) {
+		rets = -ENODEV;
+		goto Err_free_lock;
+	}
+
+	dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
+
+	switch (cmd) {
+	case IOCTL_MEI_CONNECT_CLIENT:
+		dev_dbg(&dev->pdev->dev, ": IOCTL_MEI_CONNECT_CLIENT.\n");
+
+		connect_data = kzalloc(sizeof(struct mei_connect_client_data),
+								GFP_KERNEL);
+
+		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 Err_free_mem;
+		}
+		rets = mei_ioctl_connect_client(dev, if_num,
+								connect_data,
+								file);
+
+		dev_dbg(&dev->pdev->dev, "copy connect data to user\n");
+		/* if all is ok, copying the data back to user. */
+		if (!rets) {
+			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 Err_free_mem;
+			}
+		}
+
+		break;
+
+	default:
+		rets = -EINVAL;
+		break;
+	}
+
+Err_free_mem:
+	kfree(connect_data);
+
+Err_free_lock:
+	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_file_private *file_ext = file->private_data;
+	struct mei_device *dev;
+	int if_num = iminor(file->f_dentry->d_inode);
+	unsigned int mask = 0;
+
+	if (!mei_device)
+		return mask;
+
+	dev = pci_get_drvdata(mei_device);
+
+	if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
+		return mask;
+
+	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 = 0;
+
+	if (!dev)
+		return -ENODEV;
+	mutex_lock(&dev->device_lock);
+	/* Stop watchdog if exists */
+	cancel_delayed_work(&dev->wd_work);
+	if (dev->wd_file_ext.state == MEI_FILE_CONNECTED && dev->wd_timeout) {
+		u16 wd_timeout = dev->wd_timeout;
+		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_file_ext)) {
+			dev->mei_host_buffer_is_empty = 0;
+			if (!mei_send_wd(dev))
+				dev_dbg(&dev->pdev->dev, "send stop WD failed\n");
+			else
+				mei_flow_ctrl_reduce(dev, &dev->wd_file_ext);
+
+			dev->wd_pending = 0;
+		} else {
+			dev->wd_pending = 1;
+		}
+		dev->wd_stopped = 0;
+
+		mutex_unlock(&dev->device_lock);
+		err = 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 %d.\n", err);
+			err = 0;
+		}
+		dev->wd_timeout = wd_timeout;
+	}
+	/* 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) {
+
+		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));
+		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/char/mei/mei.h b/drivers/char/mei/mei.h
new file mode 100644
index 0000000..d9eb634
--- /dev/null
+++ b/drivers/char/mei/mei.h
@@ -0,0 +1,156 @@
+/*
+ *
+ * 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_start_wd_params[];
+extern const u8 mei_stop_wd_params[];
+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_file_private *mei_alloc_file_private(struct file *file);
+int mei_disconnect_host_client(struct mei_device *dev,
+				struct mei_file_private *file_ext);
+void mei_initialize_list(struct io_mei_list *list,
+			  struct mei_device *dev);
+void mei_flush_list(struct io_mei_list *list,
+		     struct mei_file_private *file_ext);
+void mei_flush_queues(struct mei_device *dev,
+		       struct mei_file_private *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_wd(struct mei_device *dev);
+void host_init_iamthif(struct mei_device *dev);
+
+/*
+ *  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 mei_device *dev, int if_num,
+					struct mei_connect_client_data *data,
+					struct file *file);
+
+int mei_start_read(struct mei_device *dev, int if_num,
+		    struct mei_file_private *file_ext);
+
+int amthi_write(struct mei_device *dev,
+	       struct mei_cb_private *priv_cb);
+
+int amthi_read(struct mei_device *dev, int if_num, struct file *file,
+	      char __user *ubuf, size_t length, loff_t *offset);
+
+struct mei_cb_private *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_cb_private *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_file_private *fe1,
+				  const struct mei_file_private *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/char/mei/mei_version.h b/drivers/char/mei/mei_version.h
new file mode 100644
index 0000000..075bad8
--- /dev/null
+++ b/drivers/char/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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 2/7] char/mei: Interrupt handling.
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
  2011-03-22 10:51 ` [PATCH 1/7] char/mei: PCI device and char driver support Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 10:51 ` [PATCH 3/7] char/mei: MEI "Link" layer code - MEI Hardware communications Oren Weil
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/mei/interrupt.c | 1582 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1582 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/mei/interrupt.c

diff --git a/drivers/char/mei/interrupt.c b/drivers/char/mei/interrupt.c
new file mode 100644
index 0000000..f6f65e6
--- /dev/null
+++ b/drivers/char/mei/interrupt.c
@@ -0,0 +1,1582 @@
+/*
+ *
+ * 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 "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_file_private *file_ext,
+				struct mei_cb_private *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_cb_private *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");
+	} 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 io_mei_list *complete_list,
+		struct mei_device *dev,
+		struct mei_msg_hdr *mei_hdr)
+{
+	struct mei_file_private *file_ext;
+	struct mei_cb_private *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_file_private *)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_file_private *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 io_mei_list *complete_list,
+		struct mei_device *dev,
+		struct mei_msg_hdr *mei_hdr)
+{
+	struct mei_file_private *file_ext;
+	struct mei_cb_private *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_file_private *)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_cb_private *priv_cb_pos,
+			struct mei_file_private *file_ext,
+			struct io_mei_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_file_private *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_file_private *file_ext;
+	struct mei_cb_private *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_file_ext), 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_file_private *)
+					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_file_private *file_ext;
+	struct mei_cb_private *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_file_private *)
+				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_file_private *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_file_private *file_pos = NULL;
+	struct mei_file_private *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_file_private *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_file_private *file_pos = NULL;
+	struct mei_file_private *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_file_ext) {
+				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_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_cb_private *priv_cb_pos,
+			struct mei_file_private *file_ext,
+			struct io_mei_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_cb_private *priv_cb_pos,
+			struct mei_file_private *file_ext,
+			struct io_mei_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_cb_private *priv_cb_pos,
+			struct mei_file_private *file_ext,
+			struct io_mei_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_cb_private *priv_cb_pos,
+			struct mei_file_private *file_ext,
+			struct io_mei_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 io_mei_list *cmpl_list,
+		struct mei_device *dev,
+		s32 *slots)
+{
+	struct mei_msg_hdr *mei_hdr;
+	struct mei_file_private *file_pos = NULL;
+	struct mei_file_private *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 io_mei_list *cmpl_list,
+		struct mei_device *dev,
+		s32 *slots)
+{
+
+	struct mei_file_private *file_ext;
+	struct mei_cb_private *priv_cb_pos = NULL, *priv_cb_next = NULL;
+	struct io_mei_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_file_private *)
+					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_file_ext)) {
+			if (!mei_send_wd(dev))
+				dev_dbg(&dev->pdev->dev, "wd send failed.\n");
+			else
+				mei_flow_ctrl_reduce(dev, &dev->wd_file_ext);
+
+			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_file_private *)
+				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_file_private *)
+					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)
+{
+	struct mei_file_private *file_pos = NULL;
+	struct mei_file_private *file_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_file_ext.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_file_ext)) {
+				dev->mei_host_buffer_is_empty = 0;
+				dev_dbg(&dev->pdev->dev, "send watchdog.\n");
+				if (!mei_send_wd(dev)) {
+					dev_dbg(&dev->pdev->dev, "wd send failed.\n");
+				} else {
+					mei_flow_ctrl_reduce(dev,
+							 &dev->wd_file_ext);
+				}
+
+				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);
+		}
+	}
+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 io_mei_list complete_list;
+	struct mei_cb_private *cb_pos = NULL, *cb_next = NULL;
+	struct mei_file_private *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_file_private *)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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 3/7] char/mei: MEI "Link" layer code - MEI Hardware communications.
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
  2011-03-22 10:51 ` [PATCH 1/7] char/mei: PCI device and char driver support Oren Weil
  2011-03-22 10:51 ` [PATCH 2/7] char/mei: Interrupt handling Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 10:51 ` [PATCH 4/7] char/mei: MEI driver init flow Oren Weil
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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 <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/mei/interface.c |  478 ++++++++++++++++++++++++++++++++++++++++++
 drivers/char/mei/interface.h |  122 +++++++++++
 2 files changed, 600 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/mei/interface.c
 create mode 100644 drivers/char/mei/interface.h

diff --git a/drivers/char/mei/interface.c b/drivers/char/mei/interface.c
new file mode 100644
index 0000000..0712b6f
--- /dev/null
+++ b/drivers/char/mei/interface.c
@@ -0,0 +1,478 @@
+/*
+ *
+ * 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
+ */
+s32 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;
+
+	if (filled_slots > buffer_depth) {
+		/* overflow */
+		return -EOVERFLOW;
+	}
+
+	return (s32) 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
+ */
+s32 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);
+
+	if (filled_slots > buffer_depth) {
+		/* overflow */
+		return -EOVERFLOW;
+	}
+
+	dev_dbg(&dev->pdev->dev, "filled_slots =%08x\n", filled_slots);
+	return (s32) 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_file_private *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_file_private *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_file_private *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_file_private *file_ext)
+{
+	struct mei_file_private *file_pos = NULL;
+	struct mei_file_private *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_send_wd - sends watch dog message to fw.
+ *
+ * @dev: the device structure
+ *
+ * returns 1 if success, 0 - otherwise.
+ */
+int mei_send_wd(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_file_ext.host_client_id;
+	mei_hdr->me_addr = dev->wd_file_ext.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 {
+		BUG_ON(memcmp(dev->wd_data, mei_stop_wd_params,
+			MEI_WD_PARAMS_SIZE));
+		mei_hdr->length = MEI_WD_PARAMS_SIZE;
+	}
+
+	if (!mei_write_message(dev, mei_hdr, dev->wd_data, mei_hdr->length))
+		return 0;
+
+	return 1;
+}
+
+/**
+ * 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_file_private *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_file_private *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/char/mei/interface.h b/drivers/char/mei/interface.h
new file mode 100644
index 0000000..840d197
--- /dev/null
+++ b/drivers/char/mei/interface.h
@@ -0,0 +1,122 @@
+/*
+ *
+ * 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);
+
+s32 mei_count_full_read_slots(struct mei_device *dev);
+
+s32 mei_count_empty_write_slots(struct mei_device *dev);
+
+int mei_flow_ctrl_creds(struct mei_device *dev,
+				   struct mei_file_private *file_ext);
+
+int mei_send_wd(struct mei_device *dev);
+
+void mei_flow_ctrl_reduce(struct mei_device *dev,
+			 struct mei_file_private *file_ext);
+
+int mei_send_flow_control(struct mei_device *dev,
+				 struct mei_file_private *file_ext);
+
+int mei_disconnect(struct mei_device *dev,
+			  struct mei_file_private *file_ext);
+int mei_other_client_is_connecting(struct mei_device *dev,
+				     struct mei_file_private *file_ext);
+int mei_connect(struct mei_device *dev,
+		       struct mei_file_private *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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 4/7] char/mei: MEI driver init flow.
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (2 preceding siblings ...)
  2011-03-22 10:51 ` [PATCH 3/7] char/mei: MEI "Link" layer code - MEI Hardware communications Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 10:51 ` [PATCH 5/7] char/mei: Hardware and MEI driver internal struct definition Oren Weil
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/mei/init.c |  834 +++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 834 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/mei/init.c

diff --git a/drivers/char/mei/init.c b/drivers/char/mei/init.c
new file mode 100644
index 0000000..4d9bc55
--- /dev/null
+++ b/drivers/char/mei/init.c
@@ -0,0 +1,834 @@
+/*
+ *
+ * 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/moduleparam.h>
+
+#include "hw.h"
+#include "interface.h"
+#include "mei.h"
+
+/*
+ * MEI Watchdog Module Parameters
+ */
+static int watchdog_timeout = AMT_WD_VALUE;
+module_param(watchdog_timeout, int, 0);
+MODULE_PARM_DESC(watchdog_timeout,
+		"Intel(R) AMT Watchdog timeout value in seconds. (default="
+					__MODULE_STRING(AMT_WD_VALUE)
+					", disable=0)");
+
+const u8 mei_start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
+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);
+
+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 io_mei_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_file_private *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 io_mei_list *list,
+		struct mei_file_private *file_ext)
+{
+	struct mei_file_private *file_ext_tmp;
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *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_file_private *)
+				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_file_ext.file = NULL;
+	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_file_ext.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;
+
+	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)
+		err = wait_event_interruptible_timeout(dev->wait_recvd_msg,
+			dev->recvd_msg, MEI_INTEROP_TIMEOUT);
+
+	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");
+		return -ENODEV;
+	} else {
+		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");
+			return -ENODEV;
+		}
+	}
+	if (dev->version.major_version != HBM_MAJOR_VERSION ||
+	    dev->version.minor_version != HBM_MINOR_VERSION) {
+		dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
+		return -ENODEV;
+	}
+	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");
+	return 0;
+}
+
+/**
+ * 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_file_private *file_pos = NULL;
+	struct mei_file_private *file_next = NULL;
+	struct mei_cb_private *priv_cb_pos = NULL;
+	struct mei_cb_private *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_file_ext.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=%lx.\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;
+
+	host_init_wd(dev);
+	return;
+}
+
+/**
+ * mei_init_file_private - initializes private file structure.
+ *
+ * @priv: private file structure to be initialized
+ * @file: the file structure
+ */
+static void mei_init_file_private(struct mei_file_private *priv,
+				   struct file *file)
+{
+	memset(priv, 0, sizeof(struct mei_file_private));
+	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;
+}
+
+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
+ */
+static u8 mei_find_me_client_update_filext(struct mei_device *dev,
+				struct mei_file_private *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_wd - mei initialization wd.
+ *
+ * @dev: the device structure
+ */
+void host_init_wd(struct mei_device *dev)
+{
+	mei_init_file_private(&dev->wd_file_ext, NULL);
+
+	/* look for WD client and connect to it */
+	dev->wd_file_ext.state = MEI_FILE_DISCONNECTED;
+	dev->wd_timeout = 0;
+
+	if (watchdog_timeout > 0) {
+		/* AMT mode */
+		dev->wd_timeout = watchdog_timeout;
+		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));
+
+		/* find ME WD client */
+		mei_find_me_client_update_filext(dev, &dev->wd_file_ext,
+					&mei_wd_guid, MEI_WD_HOST_CLIENT_ID);
+
+		dev_dbg(&dev->pdev->dev, "check wd_file_ext\n");
+		if (MEI_FILE_CONNECTING == dev->wd_file_ext.state) {
+			if (!mei_connect(dev, &dev->wd_file_ext)) {
+				dev_dbg(&dev->pdev->dev, "Failed to connect to WD client\n");
+				dev->wd_file_ext.state = MEI_FILE_DISCONNECTED;
+				dev->wd_file_ext.host_client_id = 0;
+				host_init_iamthif(dev) ;
+			} else {
+				dev->wd_file_ext.timer_count = CONNECT_TIMEOUT;
+			}
+		} else {
+			dev_dbg(&dev->pdev->dev, "Failed to find WD client\n");
+			host_init_iamthif(dev) ;
+		}
+	} else {
+		dev->wd_bypass = 1;
+		dev_dbg(&dev->pdev->dev, "WD requested to be disabled\n");
+		host_init_iamthif(dev) ;
+	}
+}
+
+
+/**
+ * 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, NULL);
+	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_file_private *mei_alloc_file_private(struct file *file)
+{
+	struct mei_file_private *priv;
+
+	priv = kmalloc(sizeof(struct mei_file_private), GFP_KERNEL);
+	if (!priv)
+		return NULL;
+
+	mei_init_file_private(priv, file);
+
+	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_file_private *file_ext)
+{
+	int rets, err;
+	long timeout = 15;	/* 15 seconds */
+	struct mei_cb_private *priv_cb;
+
+	if (!dev || !file_ext)
+		return -ENODEV;
+
+	if (file_ext->state != MEI_FILE_DISCONNECTING)
+		return 0;
+
+	priv_cb = kzalloc(sizeof(struct mei_cb_private), 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_file_private *file_pos = NULL;
+	struct mei_file_private *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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 5/7] char/mei: Hardware and MEI driver internal struct definition
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (3 preceding siblings ...)
  2011-03-22 10:51 ` [PATCH 4/7] char/mei: MEI driver init flow Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 10:51 ` [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct) Oren Weil
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/mei/hw.h |  511 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 511 insertions(+), 0 deletions(-)
 create mode 100644 drivers/char/mei/hw.h

diff --git a/drivers/char/mei/hw.h b/drivers/char/mei/hw.h
new file mode 100644
index 0000000..4d99bfc
--- /dev/null
+++ b/drivers/char/mei/hw.h
@@ -0,0 +1,511 @@
+/*
+ *
+ * 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_cb_private {
+	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;
+};
+
+/* Private file struct */
+struct mei_file_private {
+	struct list_head link;
+	struct file *file;
+	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_cb_private *read_cb;
+};
+
+struct io_mei_list {
+	struct mei_cb_private 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 io_mei_list *io_list_array[MEI_IO_LISTS_NUMBER];
+	struct io_mei_list read_list;		/* driver read queue */
+	struct io_mei_list write_list;		/* driver write queue */
+	struct io_mei_list write_waiting_list;	/* write waiting queue */
+	struct io_mei_list ctrl_wr_list;	/* managed write IOCTL list */
+	struct io_mei_list ctrl_rd_list;	/* managed read IOCTL list */
+	struct io_mei_list amthi_cmd_list;	/* amthi list for cmd waiting */
+
+	/* driver managed amthi list for reading completed amthi cmd data */
+	struct io_mei_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_file_private wd_file_ext;
+	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;
+	int wd_bypass;	/* if 1, don't refresh watchdog ME client */
+
+	struct file *iamthif_file_object;
+	struct mei_file_private iamthif_file_ext;
+	int iamthif_ioctl;
+	int iamthif_canceled;
+	int iamthif_mtu;
+	u32 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_cb_private *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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct)
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (4 preceding siblings ...)
  2011-03-22 10:51 ` [PATCH 5/7] char/mei: Hardware and MEI driver internal struct definition Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-22 16:26   ` Greg KH
  2011-03-22 10:51 ` [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile Oren Weil
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 include/linux/mei.h |  109 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 109 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..1ac6a41
--- /dev/null
+++ b/include/linux/mei.h
@@ -0,0 +1,109 @@
+/*
+
+  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.
+	  linux-mei@linux.intel.com
+	  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 use to associate the current file descriptor with a
+ * FW Client (given by UUID). Before this IOCTL is invoked the
+ * "communication channel" (file descriptor) between Host client and
+ * FW client is not valid. Only after invoking this IOCTL a "connection" is
+ * establish to the FW client. From this point every read and write will
+ * communicate with the FW client that was associated.
+ * Only in close() (file_operation release()) the communication between
+ * the clients is disconnected.
+ *
+ * The IOCTL argument is a struct with a union the contain the input parameter
+ * and out parameter for this IOCTL.
+ *
+ * The input parameter is UUID for the FW Client.
+ * The output parameter is the FW client properties that we just connected
+ * (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];
+} __packed;
+
+/*
+ * IOCTL Connect Client Data structure
+ */
+struct mei_connect_client_data {
+	union {
+		uuid_le in_client_uuid;
+		struct mei_client out_client_propeties;
+	} d;
+} __packed;
+
+
+#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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (5 preceding siblings ...)
  2011-03-22 10:51 ` [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct) Oren Weil
@ 2011-03-22 10:51 ` Oren Weil
  2011-03-23 21:43   ` Valdis.Kletnieks
  2011-03-22 16:23 ` [PATCH 0/7] char/mei: Intel MEI Driver Greg KH
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 30+ messages in thread
From: Oren Weil @ 2011-03-22 10:51 UTC (permalink / raw)
  To: gregkh; +Cc: linux-kernel, alan, david, Oren Weil

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

Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Itzhak Tzeel-Krupp <itzhak.tzeel-krupp@intel.com>
Signed-off-by: Oren Weil <oren.jer.weil@intel.com>
---
 drivers/char/Kconfig      |   12 +++++++++++-
 drivers/char/Makefile     |    1 +
 drivers/char/mei/Makefile |   19 +++++++++++++++++++
 3 files changed, 31 insertions(+), 1 deletions(-)
 create mode 100644 drivers/char/mei/Makefile

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 04f8b2d..694dc0e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -608,5 +608,15 @@ config RAMOOPS
 	  This enables panic and oops messages to be logged to a circular
 	  buffer in RAM where it can be read back at some later point.
 
-endmenu
+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/>
+
+endmenu
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 057f654..778b816 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -61,4 +61,5 @@ obj-$(CONFIG_PS3_FLASH)		+= ps3flash.o
 obj-$(CONFIG_RAMOOPS)		+= ramoops.o
 
 obj-$(CONFIG_JS_RTC)		+= js-rtc.o
+obj-$(CONFIG_INTEL_MEI)		+= mei/
 js-rtc-y = rtc.o
diff --git a/drivers/char/mei/Makefile b/drivers/char/mei/Makefile
new file mode 100644
index 0000000..5a6b011
--- /dev/null
+++ b/drivers/char/mei/Makefile
@@ -0,0 +1,19 @@
+#
+# 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
-- 
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.


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (6 preceding siblings ...)
  2011-03-22 10:51 ` [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile Oren Weil
@ 2011-03-22 16:23 ` Greg KH
  2011-03-23 12:25   ` Weil, Oren jer
  2011-03-22 16:50 ` Arnd Bergmann
  2011-04-04 15:54 ` Pavel Machek
  9 siblings, 1 reply; 30+ messages in thread
From: Greg KH @ 2011-03-22 16:23 UTC (permalink / raw)
  To: Oren Weil; +Cc: linux-kernel, alan, david

On Tue, Mar 22, 2011 at 12:51:25PM +0200, 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.

Why not use a misc device node instead of claiming your own major
number?  That would solve your sysfs code solution for you as well
(enabling you to delete it all.)

>  drivers/char/mei/Makefile      |   19 +

How about putting it under drivers/misc/mei/ instead?  That's where
other monitoring drivers like this are at (ibmasm for example.)

Are you wanting this version reviewed for inclusion in .40?  If you
don't feel the code is ready for the main kernel tree, do you want it in
staging for the .40 release?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct)
  2011-03-22 10:51 ` [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct) Oren Weil
@ 2011-03-22 16:26   ` Greg KH
  2011-03-22 16:41     ` Arnd Bergmann
  0 siblings, 1 reply; 30+ messages in thread
From: Greg KH @ 2011-03-22 16:26 UTC (permalink / raw)
  To: Oren Weil; +Cc: linux-kernel, alan, david

On Tue, Mar 22, 2011 at 12:51:31PM +0200, Oren Weil wrote:
> +/*
> + * Intel MEI client information struct
> + */
> +struct mei_client {
> +	u32 max_msg_length;
> +	u8 protocol_version;
> +	u8 reserved[3];
> +} __packed;

As this is passing the kernel/user boundry, you need to use the proper
data types for it.

So use __u32 and __u8 here please.

> +/*
> + * IOCTL Connect Client Data structure
> + */
> +struct mei_connect_client_data {
> +	union {
> +		uuid_le in_client_uuid;
> +		struct mei_client out_client_propeties;
> +	} d;

Why not an anonymous union?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct)
  2011-03-22 16:26   ` Greg KH
@ 2011-03-22 16:41     ` Arnd Bergmann
  0 siblings, 0 replies; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-22 16:41 UTC (permalink / raw)
  To: Greg KH; +Cc: Oren Weil, linux-kernel, alan, david

On Tuesday 22 March 2011, Greg KH wrote:
> On Tue, Mar 22, 2011 at 12:51:31PM +0200, Oren Weil wrote:
> > +/*
> > + * Intel MEI client information struct
> > + */
> > +struct mei_client {
> > +     u32 max_msg_length;
> > +     u8 protocol_version;
> > +     u8 reserved[3];
> > +} __packed;
> 
> As this is passing the kernel/user boundry, you need to use the proper
> data types for it.
> 
> So use __u32 and __u8 here please.

Also, __packed should not be used in APIs, because it is a gcc extension
and makes the code less efficient.

> > +/*
> > + * IOCTL Connect Client Data structure
> > + */
> > +struct mei_connect_client_data {
> > +     union {
> > +             uuid_le in_client_uuid;
> > +             struct mei_client out_client_propeties;
> > +     } d;
> 
> Why not an anonymous union?

That would also incompatible with some compilers. I would recommend
not using a union at all, but simply to put all in one struct.

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (7 preceding siblings ...)
  2011-03-22 16:23 ` [PATCH 0/7] char/mei: Intel MEI Driver Greg KH
@ 2011-03-22 16:50 ` Arnd Bergmann
  2011-03-23  7:20   ` Weil, Oren jer
  2011-04-04 15:54 ` Pavel Machek
  9 siblings, 1 reply; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-22 16:50 UTC (permalink / raw)
  To: Oren Weil; +Cc: gregkh, linux-kernel, alan, david

On Tuesday 22 March 2011, Oren Weil wrote:
> 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.

I suppose one of the important things to do here is integrate
the watchdog part into the common watchdog infrastructure
in drivers/watchdog. Maybe this driver can export a simple
interface to another module that implements the watchdog
device?

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 10:51 ` [PATCH 1/7] char/mei: PCI device and char driver support Oren Weil
@ 2011-03-22 17:22   ` Arnd Bergmann
  2011-03-22 21:57     ` Alan Cox
  0 siblings, 1 reply; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-22 17:22 UTC (permalink / raw)
  To: Oren Weil; +Cc: gregkh, linux-kernel, alan, david

On Tuesday 22 March 2011, Oren Weil wrote:
> +static ssize_t mei_read(struct file *file, char __user *ubuf,
> +                        size_t length, loff_t *offset)
> +{
> +       int i;
> +       int rets;
> +       int err;
> +       int if_num = iminor(file->f_dentry->d_inode);
> +       struct mei_file_private *file_ext = file->private_data;
> +       struct mei_cb_private *priv_cb_pos = NULL;
> +       struct mei_cb_private *priv_cb = NULL;
> +       struct mei_device *dev;
> +
> +       if (!mei_device)
> +               return -ENODEV;
> +
> +       dev = pci_get_drvdata(mei_device);
> +       if (if_num != MEI_MINOR_NUMBER || !dev || !file_ext)
> +               return -ENODEV;
> +

I'm very confused by this code and how you support multiple
instances of PCI devices, cb_list entries and concurrent connections:

* You look up a global pointer to mei_device, when there could
  be multiple PCI devices.

* You use the minor number of the chardev as an index throughout
  the code, but bail out if it does not match a constant.

* You walk a list of mei_cb_private entries when you should have
  just a single one for this file. In other places, you walk a list
  of mei_file_private entries.

I think you should try to document your data structures and how
they relate to each other, and remove all that turn out not to
be needed in the process.

>From the brief look I had at this rather complex code, I would
assume that it could look something like this:

* In the PCI probe function, create a misc character device for
  every instance you find behind each device. Remove the global
  variables and constants.

* Unify mei_file_private and mei_cb_private, allocate at open
  time but fill when the ioctl gets called.

Would that work, or am I missing something major?

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 17:22   ` Arnd Bergmann
@ 2011-03-22 21:57     ` Alan Cox
  2011-03-22 22:22       ` Greg KH
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Cox @ 2011-03-22 21:57 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: Oren Weil, gregkh, linux-kernel, david

> * In the PCI probe function, create a misc character device for
>   every instance you find behind each device. Remove the global
>   variables and constants.

The current code is complex and could be a ton simpler *but* don't
abuse misc devices when you have one per PCI instance please.

Alan (Miscdevice police)

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 21:57     ` Alan Cox
@ 2011-03-22 22:22       ` Greg KH
  2011-03-22 23:04         ` Alan Cox
  0 siblings, 1 reply; 30+ messages in thread
From: Greg KH @ 2011-03-22 22:22 UTC (permalink / raw)
  To: Alan Cox; +Cc: Arnd Bergmann, Oren Weil, linux-kernel, david

On Tue, Mar 22, 2011 at 09:57:59PM +0000, Alan Cox wrote:
> > * In the PCI probe function, create a misc character device for
> >   every instance you find behind each device. Remove the global
> >   variables and constants.
> 
> The current code is complex and could be a ton simpler *but* don't
> abuse misc devices when you have one per PCI instance please.

The code specifically says it only supports one device in the system at
a time.  Since it can't do any more than that, why wouldn't it be
correct for a misdevice to be used here?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 22:22       ` Greg KH
@ 2011-03-22 23:04         ` Alan Cox
  2011-03-23  7:59           ` Arnd Bergmann
  0 siblings, 1 reply; 30+ messages in thread
From: Alan Cox @ 2011-03-22 23:04 UTC (permalink / raw)
  To: Greg KH; +Cc: Alan Cox, Arnd Bergmann, Oren Weil, linux-kernel, david

On Tue, 22 Mar 2011 15:22:59 -0700
Greg KH <gregkh@suse.de> wrote:

> On Tue, Mar 22, 2011 at 09:57:59PM +0000, Alan Cox wrote:
> > > * In the PCI probe function, create a misc character device for
> > >   ". Remove the global
> > >   variables and constants.
> > 
> > The current code is complex and could be a ton simpler *but* don't
> > abuse misc devices when you have one per PCI instance please.
> 
> The code specifically says it only supports one device in the system at
> a time.  Since it can't do any more than that, why wouldn't it be
> correct for a misdevice to be used here?

Arnd was suggesting one device for "every instance you find behind each
device" so that isn't a good idea for a misc device given there will be
lots of them.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-22 16:50 ` Arnd Bergmann
@ 2011-03-23  7:20   ` Weil, Oren jer
  2011-03-23 13:51     ` Greg KH
  0 siblings, 1 reply; 30+ messages in thread
From: Weil, Oren jer @ 2011-03-23  7:20 UTC (permalink / raw)
  To: Arnd Bergmann; +Cc: gregkh, linux-kernel, alan, david


[-- Attachment #1.1: Type: text/plain, Size: 352 bytes --]

>I suppose one of the important things to do here is integrate the watchdog
>part into the common watchdog infrastructure in drivers/watchdog. Maybe
>this driver can export a simple interface to another module that implements
>the watchdog device?

Yes, this is one the ideas the Alan suggested, 
we are thinking about this direction in the future.

 

[-- Attachment #1.2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 8587 bytes --]

[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 1/7] char/mei: PCI device and char driver support.
  2011-03-22 23:04         ` Alan Cox
@ 2011-03-23  7:59           ` Arnd Bergmann
  0 siblings, 0 replies; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-23  7:59 UTC (permalink / raw)
  To: Alan Cox; +Cc: Greg KH, Alan Cox, Oren Weil, linux-kernel, david

On Wednesday 23 March 2011, Alan Cox wrote:
> On Tue, 22 Mar 2011 15:22:59 -0700
> Greg KH <gregkh@suse.de> wrote:
> 
> > On Tue, Mar 22, 2011 at 09:57:59PM +0000, Alan Cox wrote:
> > > > * In the PCI probe function, create a misc character device for
> > > >   ". Remove the global
> > > >   variables and constants.
> > > 
> > > The current code is complex and could be a ton simpler but don't
> > > abuse misc devices when you have one per PCI instance please.
> > 
> > The code specifically says it only supports one device in the system at
> > a time.  Since it can't do any more than that, why wouldn't it be
> > correct for a misdevice to be used here?
> 
> Arnd was suggesting one device for "every instance you find behind each
> device" so that isn't a good idea for a misc device given there will be
> lots of them.

Yes, makes sense. I was assuming that the common case was that there
is only a single instance.

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-22 16:23 ` [PATCH 0/7] char/mei: Intel MEI Driver Greg KH
@ 2011-03-23 12:25   ` Weil, Oren jer
  2011-03-23 13:50     ` Greg KH
  0 siblings, 1 reply; 30+ messages in thread
From: Weil, Oren jer @ 2011-03-23 12:25 UTC (permalink / raw)
  To: Greg KH; +Cc: linux-kernel, alan, david


[-- Attachment #1.1: Type: text/plain, Size: 393 bytes --]


>Are you wanting this version reviewed for inclusion in .40?  If you don't 
>feel
>the code is ready for the main kernel tree, do you want it in staging for the 
>.40
>release?

Yes, we would like  this version to be reviewed for .40.
I feel that the driver is ready for main kernel tree, but we still working on 
it to make it better.

I don't think we want to put it in staging.

Thanks.



[-- Attachment #1.2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 8587 bytes --]

[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23 12:25   ` Weil, Oren jer
@ 2011-03-23 13:50     ` Greg KH
  0 siblings, 0 replies; 30+ messages in thread
From: Greg KH @ 2011-03-23 13:50 UTC (permalink / raw)
  To: Weil, Oren jer; +Cc: linux-kernel, alan, david

On Wed, Mar 23, 2011 at 02:25:18PM +0200, Weil, Oren jer wrote:
> 
> >Are you wanting this version reviewed for inclusion in .40?  If you don't 
> >feel
> >the code is ready for the main kernel tree, do you want it in staging for the 
> >.40
> >release?
> 
> Yes, we would like  this version to be reviewed for .40.
> I feel that the driver is ready for main kernel tree, but we still working on 
> it to make it better.
> 
> I don't think we want to put it in staging.

Why not?  It needs lots of work, as you have noted, are you going to be
able to get it all cleaned up in the next few weeks properly?

If so, great, I'll wait for your next round of patches to take a look at
them again.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23  7:20   ` Weil, Oren jer
@ 2011-03-23 13:51     ` Greg KH
  2011-03-23 15:55       ` Weil, Oren jer
  0 siblings, 1 reply; 30+ messages in thread
From: Greg KH @ 2011-03-23 13:51 UTC (permalink / raw)
  To: Weil, Oren jer; +Cc: Arnd Bergmann, linux-kernel, alan, david

On Wed, Mar 23, 2011 at 09:20:20AM +0200, Weil, Oren jer wrote:
> >I suppose one of the important things to do here is integrate the watchdog
> >part into the common watchdog infrastructure in drivers/watchdog. Maybe
> >this driver can export a simple interface to another module that implements
> >the watchdog device?
> 
> Yes, this is one the ideas the Alan suggested, 
> we are thinking about this direction in the future.

What's keeping you from doing this now?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23 13:51     ` Greg KH
@ 2011-03-23 15:55       ` Weil, Oren jer
  2011-03-23 16:06         ` Greg KH
  0 siblings, 1 reply; 30+ messages in thread
From: Weil, Oren jer @ 2011-03-23 15:55 UTC (permalink / raw)
  To: Greg KH; +Cc: Arnd Bergmann, linux-kernel, alan, david


[-- Attachment #1.1: Type: text/plain, Size: 579 bytes --]

>> >I suppose one of the important things to do here is integrate the
>> >watchdog part into the common watchdog infrastructure in
>> >drivers/watchdog. Maybe this driver can export a simple interface to
>> >another module that implements the watchdog device?
>>
>> Yes, this is one the ideas the Alan suggested, we are thinking about
>> this direction in the future.
>
>What's keeping you from doing this now?

We need to change the driver to expose functions to communicate with the FW
Client (feature) 
>From other kernel models, the design for now is only from user space.



[-- Attachment #1.2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 8587 bytes --]

[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23 15:55       ` Weil, Oren jer
@ 2011-03-23 16:06         ` Greg KH
  2011-03-23 16:20           ` Arnd Bergmann
  0 siblings, 1 reply; 30+ messages in thread
From: Greg KH @ 2011-03-23 16:06 UTC (permalink / raw)
  To: Weil, Oren jer; +Cc: Arnd Bergmann, linux-kernel, alan, david

On Wed, Mar 23, 2011 at 05:55:47PM +0200, Weil, Oren jer wrote:
> >> >I suppose one of the important things to do here is integrate the
> >> >watchdog part into the common watchdog infrastructure in
> >> >drivers/watchdog. Maybe this driver can export a simple interface to
> >> >another module that implements the watchdog device?
> >>
> >> Yes, this is one the ideas the Alan suggested, we are thinking about
> >> this direction in the future.
> >
> >What's keeping you from doing this now?
> 
> We need to change the driver to expose functions to communicate with the FW
> Client (feature) 
> From other kernel models, the design for now is only from user space.

So you need to add support to the watchdog infrastructure for this?  For
some reason I thought you could kick the watchdog from within kernel
code, did I get this wrong?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23 16:06         ` Greg KH
@ 2011-03-23 16:20           ` Arnd Bergmann
  2011-03-24 12:54             ` Weil, Oren jer
  0 siblings, 1 reply; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-23 16:20 UTC (permalink / raw)
  To: Greg KH; +Cc: Weil, Oren jer, linux-kernel, alan, david

On Wednesday 23 March 2011, Greg KH wrote:
> On Wed, Mar 23, 2011 at 05:55:47PM +0200, Weil, Oren jer wrote:
> > >> >I suppose one of the important things to do here is integrate the
> > >> >watchdog part into the common watchdog infrastructure in
> > >> >drivers/watchdog. Maybe this driver can export a simple interface to
> > >> >another module that implements the watchdog device?
> > >>
> > >> Yes, this is one the ideas the Alan suggested, we are thinking about
> > >> this direction in the future.
> > >
> > >What's keeping you from doing this now?
> > 
> > We need to change the driver to expose functions to communicate with the FW
> > Client (feature) 
> > From other kernel models, the design for now is only from user space.

For a simple example, look at drivers/watchdog/riowd.c (most other
files in that directory do as well), just replace the riowd_writereg()
function in there with one that calls an exported function from your
hardware. This should really be trivial to do.
 
> So you need to add support to the watchdog infrastructure for this?  For
> some reason I thought you could kick the watchdog from within kernel
> code, did I get this wrong?

A watchdog must be triggered from user space, in Linux we do this by
writing to /dev/watchdog. If you did it from kernel space, the watchdog
would not detect the case where the system is half-broken and still
able to trigger the watchdog, but not do much beyond that.

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile
  2011-03-22 10:51 ` [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile Oren Weil
@ 2011-03-23 21:43   ` Valdis.Kletnieks
  0 siblings, 0 replies; 30+ messages in thread
From: Valdis.Kletnieks @ 2011-03-23 21:43 UTC (permalink / raw)
  To: Oren Weil; +Cc: gregkh, linux-kernel, alan, david

[-- Attachment #1: Type: text/plain, Size: 850 bytes --]

On Tue, 22 Mar 2011 12:51:32 +0200, Oren Weil said:
> Changes of Kconfig and Makefile in driver/char
> for adding the Intel MEI Driver to the Linux kernel.
> New Makefile building for MEI Driver.

> +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.

Can we have this re-worded so it doesn't have an implied "all Intel chipsets",
perhaps adding "chipsets that support the Intel vPro technology", or similar?

(And though I found that at least *some* vPro-capable chipsets do IME, I didn't
find a statement that *all* vPro will do it, so I'm still confused, thus no
patch from me...)


[-- Attachment #2: Type: application/pgp-signature, Size: 227 bytes --]

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-23 16:20           ` Arnd Bergmann
@ 2011-03-24 12:54             ` Weil, Oren jer
  2011-03-24 13:10               ` Arnd Bergmann
  0 siblings, 1 reply; 30+ messages in thread
From: Weil, Oren jer @ 2011-03-24 12:54 UTC (permalink / raw)
  To: Arnd Bergmann, Greg KH; +Cc: linux-kernel, alan, david


[-- Attachment #1.1: Type: text/plain, Size: 1538 bytes --]

>For a simple example, look at drivers/watchdog/riowd.c (most other files in
>that directory do as well), just replace the riowd_writereg() function in
there
>with one that calls an exported function from your hardware. This should
>really be trivial to do.

our Watchdog is a FW Client (FW Feature) so in order to communicate with it
we need 
use the MEI Driver functions. (Send Connect Message, Send and Recv Data).
AFAIK, I can't use sys_open/sys_write to communicate with the MEI Driver, I
need to expose
functions that handles all of that communication, right?

>> So you need to add support to the watchdog infrastructure for this?
>> For some reason I thought you could kick the watchdog from within
>> kernel code, did I get this wrong?

>A watchdog must be triggered from user space, in Linux we do this by
writing
>to /dev/watchdog. If you did it from kernel space, the watchdog would not
>detect the case where the system is half-broken and still able to trigger
the
>watchdog, but not do much beyond that.

The Intel AMT Watchdog is not like a regular Watchdog,  our interface is
like regular watchdog (set timeout API, ping  API and etc..)
But when the watchdog expired the system will not be reboot, the AMT
Watchdog is sending a OOB Message/Event
to remote management console and the console software can decide what to do.
(it can notify the operator to check why
the system hangs, it can send a OOB reboot command to reboot the hang system
and etc...)
 
Thanks.

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



[-- Attachment #1.2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 8587 bytes --]

[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-24 12:54             ` Weil, Oren jer
@ 2011-03-24 13:10               ` Arnd Bergmann
  0 siblings, 0 replies; 30+ messages in thread
From: Arnd Bergmann @ 2011-03-24 13:10 UTC (permalink / raw)
  To: Weil, Oren jer; +Cc: Greg KH, linux-kernel, alan, david

On Thursday 24 March 2011, Weil, Oren jer wrote:
> >For a simple example, look at drivers/watchdog/riowd.c (most other files in
> >that directory do as well), just replace the riowd_writereg() function in
> there
> >with one that calls an exported function from your hardware. This should
> >really be trivial to do.
> 
> our Watchdog is a FW Client (FW Feature) so in order to communicate with it
> we need 
> use the MEI Driver functions. (Send Connect Message, Send and Recv Data).
> AFAIK, I can't use sys_open/sys_write to communicate with the MEI Driver, I
> need to expose
> functions that handles all of that communication, right?

Yes. Use EXPORT_SYMBOL_GPL().

> >> So you need to add support to the watchdog infrastructure for this?
> >> For some reason I thought you could kick the watchdog from within
> >> kernel code, did I get this wrong?
> 
> >A watchdog must be triggered from user space, in Linux we do this by
> writing
> >to /dev/watchdog. If you did it from kernel space, the watchdog would not
> >detect the case where the system is half-broken and still able to trigger
> the
> >watchdog, but not do much beyond that.
> 
> The Intel AMT Watchdog is not like a regular Watchdog,  our interface is
> like regular watchdog (set timeout API, ping  API and etc..)
> But when the watchdog expired the system will not be reboot, the AMT
> Watchdog is sending a OOB Message/Event
> to remote management console and the console software can decide what to do.
> (it can notify the operator to check why
> the system hangs, it can send a OOB reboot command to reboot the hang system
> and etc...)

Close enough, I guess.

	Arnd

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
                   ` (8 preceding siblings ...)
  2011-03-22 16:50 ` Arnd Bergmann
@ 2011-04-04 15:54 ` Pavel Machek
  2011-04-05  6:01   ` Weil, Oren jer
  9 siblings, 1 reply; 30+ messages in thread
From: Pavel Machek @ 2011-04-04 15:54 UTC (permalink / raw)
  To: Oren Weil; +Cc: gregkh, linux-kernel, alan, david

Hi!

> ďťż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.

What is expected use of MEI?

>  drivers/char/Kconfig           |   12 +-
>  drivers/char/Makefile          |    1 +
>  drivers/char/mei/Makefile      |   19 +
>  drivers/char/mei/hw.h          |  511 +++++++++++++
>  drivers/char/mei/init.c        |  834 +++++++++++++++++++++
>  drivers/char/mei/interface.c   |  478 ++++++++++++
>  drivers/char/mei/interface.h   |  122 +++
>  drivers/char/mei/interrupt.c   | 1582 ++++++++++++++++++++++++++++++++++++++++
>  drivers/char/mei/iorw.c        |  608 +++++++++++++++
>  drivers/char/mei/main.c        | 1442 ++++++++++++++++++++++++++++++++++++
>  drivers/char/mei/mei.h         |  156 ++++
>  drivers/char/mei/mei_version.h |   31 +
>  include/linux/mei.h            |  109 +++

No docs describing the interface?

> 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.

So anyone reading this through web interface needs to "delete all 
copies"?

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-04-04 15:54 ` Pavel Machek
@ 2011-04-05  6:01   ` Weil, Oren jer
  2011-04-18 13:53     ` Pavel Machek
  0 siblings, 1 reply; 30+ messages in thread
From: Weil, Oren jer @ 2011-04-05  6:01 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, alan, david


[-- Attachment #1.1: Type: text/plain, Size: 599 bytes --]

Hi

>What is expected use of MEI?

It variant between version/sku of the chipset, which version as its own set of 
features.
One of the main used of MEI is for computer manageability, to provide a local 
interface to the ME coprocessor.
Some of the Intel manageability solution involve application running on the 
local machine.

You can read more about this in:
http://software.intel.com/en-us/manageability-security/


>No docs describing the interface?


Which type of information are you looking? The HW interface?
The User Interface?


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



[-- Attachment #1.2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 8587 bytes --]

[-- Attachment #2: Type: text/plain, Size: 366 bytes --]

---------------------------------------------------------------------
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.

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH 0/7] char/mei: Intel MEI Driver
  2011-04-05  6:01   ` Weil, Oren jer
@ 2011-04-18 13:53     ` Pavel Machek
  0 siblings, 0 replies; 30+ messages in thread
From: Pavel Machek @ 2011-04-18 13:53 UTC (permalink / raw)
  To: Weil, Oren jer; +Cc: linux-kernel, alan, david

Hi!

> You can read more about this in:
> http://software.intel.com/en-us/manageability-security/

Unfortunately, this contains marketing info, not technical one.


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2011-04-18 13:53 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-22 10:51 [PATCH 0/7] char/mei: Intel MEI Driver Oren Weil
2011-03-22 10:51 ` [PATCH 1/7] char/mei: PCI device and char driver support Oren Weil
2011-03-22 17:22   ` Arnd Bergmann
2011-03-22 21:57     ` Alan Cox
2011-03-22 22:22       ` Greg KH
2011-03-22 23:04         ` Alan Cox
2011-03-23  7:59           ` Arnd Bergmann
2011-03-22 10:51 ` [PATCH 2/7] char/mei: Interrupt handling Oren Weil
2011-03-22 10:51 ` [PATCH 3/7] char/mei: MEI "Link" layer code - MEI Hardware communications Oren Weil
2011-03-22 10:51 ` [PATCH 4/7] char/mei: MEI driver init flow Oren Weil
2011-03-22 10:51 ` [PATCH 5/7] char/mei: Hardware and MEI driver internal struct definition Oren Weil
2011-03-22 10:51 ` [PATCH 6/7] char/mei: Header file contain the Userland API, (IOCTL and its struct) Oren Weil
2011-03-22 16:26   ` Greg KH
2011-03-22 16:41     ` Arnd Bergmann
2011-03-22 10:51 ` [PATCH 7/7] char/mei: Updates to char/Kconfig ane char/Makefile Oren Weil
2011-03-23 21:43   ` Valdis.Kletnieks
2011-03-22 16:23 ` [PATCH 0/7] char/mei: Intel MEI Driver Greg KH
2011-03-23 12:25   ` Weil, Oren jer
2011-03-23 13:50     ` Greg KH
2011-03-22 16:50 ` Arnd Bergmann
2011-03-23  7:20   ` Weil, Oren jer
2011-03-23 13:51     ` Greg KH
2011-03-23 15:55       ` Weil, Oren jer
2011-03-23 16:06         ` Greg KH
2011-03-23 16:20           ` Arnd Bergmann
2011-03-24 12:54             ` Weil, Oren jer
2011-03-24 13:10               ` Arnd Bergmann
2011-04-04 15:54 ` Pavel Machek
2011-04-05  6:01   ` Weil, Oren jer
2011-04-18 13:53     ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).