diff options
Diffstat (limited to 'drivers/misc/mei/interrupt.c')
| -rw-r--r-- | drivers/misc/mei/interrupt.c | 1590 | 
1 files changed, 1590 insertions, 0 deletions
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c new file mode 100644 index 00000000000..93936f1b75e --- /dev/null +++ b/drivers/misc/mei/interrupt.c @@ -0,0 +1,1590 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + */ + + +#include <linux/pci.h> +#include <linux/kthread.h> +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/jiffies.h> + +#include "mei_dev.h" +#include <linux/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. + * + * @cl: private data of the file object. + * @cb_pos: callback block. + */ +static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos) +{ +	if (cb_pos->major_file_operations == MEI_WRITE) { +		mei_free_cb_private(cb_pos); +		cb_pos = NULL; +		cl->writing_state = MEI_WRITE_COMPLETE; +		if (waitqueue_active(&cl->tx_wait)) +			wake_up_interruptible(&cl->tx_wait); + +	} else if (cb_pos->major_file_operations == MEI_READ && +			MEI_READING == cl->reading_state) { +		cl->reading_state = MEI_READ_COMPLETE; +		if (waitqueue_active(&cl->rx_wait)) +			wake_up_interruptible(&cl->rx_wait); + +	} +} + +/** + * _mei_cmpl_iamthif - processes completed iamthif operation. + * + * @dev: the device structure. + * @cb_pos: callback block. + */ +static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos) +{ +	if (dev->iamthif_canceled != 1) { +		dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; +		dev->iamthif_stall_timer = 0; +		memcpy(cb_pos->response_buffer.data, +				dev->iamthif_msg_buf, +				dev->iamthif_msg_buf_index); +		list_add_tail(&cb_pos->cb_list, +				&dev->amthi_read_complete_list.mei_cb.cb_list); +		dev_dbg(&dev->pdev->dev, "amthi read completed.\n"); +		dev->iamthif_timer = jiffies; +		dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", +				dev->iamthif_timer); +	} else { +		mei_run_next_iamthif_cmd(dev); +	} + +	dev_dbg(&dev->pdev->dev, "completing amthi call back.\n"); +	wake_up_interruptible(&dev->iamthif_cl.wait); +} + + +/** + * mei_irq_thread_read_amthi_message - bottom half read routine after ISR to + * handle the read amthi message data processing. + * + * @complete_list: An instance of our list structure + * @dev: the device structure + * @mei_hdr: header of amthi message + * + * returns 0 on success, <0 on failure. + */ +static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list, +		struct mei_device *dev, +		struct mei_msg_hdr *mei_hdr) +{ +	struct mei_cl *cl; +	struct mei_cl_cb *cb; +	unsigned char *buffer; + +	BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id); +	BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING); + +	buffer = 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; + +	cb = dev->iamthif_current_cb; +	dev->iamthif_current_cb = NULL; + +	cl = (struct mei_cl *)cb->file_private; +	if (!cl) +		return -ENODEV; + +	dev->iamthif_stall_timer = 0; +	cb->information =	dev->iamthif_msg_buf_index; +	cb->read_time = jiffies; +	if (dev->iamthif_ioctl && cl == &dev->iamthif_cl) { +		/* 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(&cb->cb_list, +						&complete_list->mei_cb.cb_list); +	} +	return 0; +} + +/** + * _mei_irq_thread_state_ok - checks if mei header matches file private data + * + * @cl: private data of the file object + * @mei_hdr: header of mei client message + * + * returns !=0 if matches, 0 if no match. + */ +static int _mei_irq_thread_state_ok(struct mei_cl *cl, +				struct mei_msg_hdr *mei_hdr) +{ +	return (cl->host_client_id == mei_hdr->host_addr && +		cl->me_client_id == mei_hdr->me_addr && +		cl->state == MEI_FILE_CONNECTED && +		MEI_READ_COMPLETE != cl->reading_state); +} + +/** + * mei_irq_thread_read_client_message - bottom half read routine after ISR to + * handle the read mei client message data processing. + * + * @complete_list: An instance of our list structure + * @dev: the device structure + * @mei_hdr: header of mei client message + * + * returns 0 on success, <0 on failure. + */ +static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list, +		struct mei_device *dev, +		struct mei_msg_hdr *mei_hdr) +{ +	struct mei_cl *cl; +	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; +	unsigned char *buffer = NULL; + +	dev_dbg(&dev->pdev->dev, "start client msg\n"); +	if (list_empty(&dev->read_list.mei_cb.cb_list)) +		goto quit; + +	list_for_each_entry_safe(cb_pos, cb_next, +			&dev->read_list.mei_cb.cb_list, cb_list) { +		cl = (struct mei_cl *)cb_pos->file_private; +		if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { +			cl->reading_state = MEI_READING; +			buffer = cb_pos->response_buffer.data + cb_pos->information; + +			if (cb_pos->response_buffer.size < +					mei_hdr->length + cb_pos->information) { +				dev_dbg(&dev->pdev->dev, "message overflow.\n"); +				list_del(&cb_pos->cb_list); +				return -ENOMEM; +			} +			if (buffer) +				mei_read_slots(dev, buffer, mei_hdr->length); + +			cb_pos->information += mei_hdr->length; +			if (mei_hdr->msg_complete) { +				cl->status = 0; +				list_del(&cb_pos->cb_list); +				dev_dbg(&dev->pdev->dev, +					"completed read host client = %d," +					"ME client = %d, " +					"data length = %lu\n", +					cl->host_client_id, +					cl->me_client_id, +					cb_pos->information); + +				*(cb_pos->response_buffer.data + +					cb_pos->information) = '\0'; +				dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n", +					cb_pos->response_buffer.data); +				list_add_tail(&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, 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))) { +		return -EMSGSIZE; +	} +	*slots -= (sizeof(struct mei_msg_hdr) + +				sizeof(struct hbm_flow_control) + 3) / 4; +	if (mei_send_flow_control(dev, &dev->iamthif_cl)) { +		dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n"); +		return -EIO; +	} + +	dev_dbg(&dev->pdev->dev, "iamthif flow control success\n"); +	dev->iamthif_state = MEI_IAMTHIF_READING; +	dev->iamthif_flow_control_pending = false; +	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; +} + +/** + * _mei_irq_thread_close - processes close related operation. + * + * @dev: the device structure. + * @slots: free slots. + * @cb_pos: callback block. + * @cl: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, +				struct mei_cl_cb *cb_pos, +				struct mei_cl *cl, +				struct mei_io_list *cmpl_list) +{ +	if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_client_disconnect_request))) { +		*slots -= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_client_disconnect_request) + 3) / 4; + +		if (mei_disconnect(dev, cl)) { +			cl->status = 0; +			cb_pos->information = 0; +			list_move_tail(&cb_pos->cb_list, +					&cmpl_list->mei_cb.cb_list); +			return -EMSGSIZE; +		} else { +			cl->state = MEI_FILE_DISCONNECTING; +			cl->status = 0; +			cb_pos->information = 0; +			list_move_tail(&cb_pos->cb_list, +					&dev->ctrl_rd_list.mei_cb.cb_list); +			cl->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. + * + * @cl: private data of the file object + * @rs: connect response bus message + * + */ +static bool is_treat_specially_client(struct mei_cl *cl, +		struct hbm_client_connect_response *rs) +{ + +	if (cl->host_client_id == rs->host_addr && +	    cl->me_client_id == rs->me_addr) { +		if (!rs->status) { +			cl->state = MEI_FILE_CONNECTED; +			cl->status = 0; + +		} else { +			cl->state = MEI_FILE_DISCONNECTED; +			cl->status = -ENODEV; +		} +		cl->timer_count = 0; + +		return true; +	} +	return false; +} + +/** + * mei_client_connect_response - connects to response irq routine + * + * @dev: the device structure + * @rs: connect response bus message + */ +static void mei_client_connect_response(struct mei_device *dev, +		struct hbm_client_connect_response *rs) +{ + +	struct mei_cl *cl; +	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; + +	dev_dbg(&dev->pdev->dev, +			"connect_response:\n" +			"ME Client = %d\n" +			"Host Client = %d\n" +			"Status = %d\n", +			rs->me_addr, +			rs->host_addr, +			rs->status); + +	/* if WD or iamthif client treat specially */ + +	if (is_treat_specially_client(&(dev->wd_cl), rs)) { +		dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); +		mei_watchdog_register(dev); + +		/* next step in the state maching */ +		mei_host_init_iamthif(dev); +		return; +	} + +	if (is_treat_specially_client(&(dev->iamthif_cl), rs)) { +		dev->iamthif_state = MEI_IAMTHIF_IDLE; +		return; +	} +	list_for_each_entry_safe(cb_pos, cb_next, +				&dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { + +		cl = (struct mei_cl *)cb_pos->file_private; +		if (!cl) { +			list_del(&cb_pos->cb_list); +			return; +		} +		if (MEI_IOCTL == cb_pos->major_file_operations) { +			if (is_treat_specially_client(cl, rs)) { +				list_del(&cb_pos->cb_list); +				cl->status = 0; +				cl->timer_count = 0; +				break; +			} +		} +	} +} + +/** + * mei_client_disconnect_response - disconnects from response irq routine + * + * @dev: the device structure + * @rs: disconnect response bus message + */ +static void mei_client_disconnect_response(struct mei_device *dev, +					struct hbm_client_connect_response *rs) +{ +	struct mei_cl *cl; +	struct mei_cl_cb *cb_pos = NULL, *cb_next = 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); + +	list_for_each_entry_safe(cb_pos, cb_next, +			&dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { +		cl = (struct mei_cl *)cb_pos->file_private; + +		if (!cl) { +			list_del(&cb_pos->cb_list); +			return; +		} + +		dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); +		if (cl->host_client_id == rs->host_addr && +		    cl->me_client_id == rs->me_addr) { + +			list_del(&cb_pos->cb_list); +			if (!rs->status) +				cl->state = MEI_FILE_DISCONNECTED; + +			cl->status = 0; +			cl->timer_count = 0; +			break; +		} +	} +} + +/** + * same_flow_addr - tells if they have the same address. + * + * @file: private data of the file object. + * @flow: flow control. + * + * returns  !=0, same; 0,not. + */ +static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow) +{ +	return (cl->host_client_id == flow->host_addr && +		cl->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->me_clients_num; i++) { +		client = &dev->me_clients[i]; +		if (client && flow->me_addr == client->client_id) { +			if (client->props.single_recv_buf) { +				client->mei_flow_ctrl_creds++; +				dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", +				    flow->me_addr); +				dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", +				    client->mei_flow_ctrl_creds); +			} else { +				BUG();	/* error in flow control */ +			} +		} +	} +} + +/** + * mei_client_flow_control_response - flow control response irq routine + * + * @dev: the device structure + * @flow_control: flow control response bus message + */ +static void mei_client_flow_control_response(struct mei_device *dev, +		struct hbm_flow_control *flow_control) +{ +	struct mei_cl *cl_pos = NULL; +	struct mei_cl *cl_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(cl_pos, cl_next, +				&dev->file_list, link) { +			dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n"); + +			dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n", +			    cl_pos->host_client_id, +			    cl_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(cl_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); +				cl_pos->mei_flow_ctrl_creds++; +				dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n", +				    cl_pos->mei_flow_ctrl_creds); +				break; +			} +		} +	} +} + +/** + * same_disconn_addr - tells if they have the same address + * + * @file: private data of the file object. + * @disconn: disconnection request. + * + * returns !=0, same; 0,not. + */ +static int same_disconn_addr(struct mei_cl *cl, +			     struct hbm_client_disconnect_request *disconn) +{ +	return (cl->host_client_id == disconn->host_addr && +		cl->me_client_id == disconn->me_addr); +} + +/** + * mei_client_disconnect_request - disconnects from request irq routine + * + * @dev: the device structure. + * @disconnect_req: disconnect request bus message. + */ +static void mei_client_disconnect_request(struct mei_device *dev, +		struct hbm_client_disconnect_request *disconnect_req) +{ +	struct mei_msg_hdr *mei_hdr; +	struct hbm_client_connect_response *disconnect_res; +	struct mei_cl *cl_pos = NULL; +	struct mei_cl *cl_next = NULL; + +	list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { +		if (same_disconn_addr(cl_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); +			cl_pos->state = MEI_FILE_DISCONNECTED; +			cl_pos->timer_count = 0; +			if (cl_pos == &dev->wd_cl) { +				dev->wd_due_counter = 0; +				dev->wd_pending = false; +			} else if (cl_pos == &dev->iamthif_cl) +				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 = cl_pos->host_client_id; +			disconnect_res->me_addr = cl_pos->me_client_id; +			disconnect_res->hbm_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; +	int res; + + +	/* read the message to our buffer */ +	BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf)); +	mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); +	mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; + +	switch (mei_msg->hbm_cmd) { +	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; +				mei_host_enum_clients_message(dev); +			} else { +				dev->recvd_msg = false; +				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 = (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_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->hbm_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 = true; +		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++; + +				/** Send Client Properties request **/ +				res = mei_host_client_properties(dev); +				if (res < 0) { +					dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed"); +					return; +				} else if (!res) { +					/* +					 * No more clients to send to. +					 * Clear Map for indicating now ME clients +					 * with associated host client +					 */ +					bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX); +					dev->open_handle_count = 0; + +					/* +					 * Reserving the first three client IDs +					 * Client Id 0 - Reserved for MEI Bus Message communications +					 * Client Id 1 - Reserved for Watchdog +					 * Client ID 2 - Reserved for AMTHI +					 */ +					bitmap_set(dev->host_clients_map, 0, 3); +					dev->mei_state = MEI_ENABLED; + +					/* if wd initialization fails, initialization the AMTHI client, +					 * otherwise the AMTHI client will be initialized after the WD client connect response +					 * will be received +					 */ +					if (mei_wd_host_init(dev)) +						mei_host_init_iamthif(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; +				mei_allocate_me_clients_storage(dev); +				dev->init_clients_state = +					MEI_CLIENT_PROPERTIES_MESSAGE; +				mei_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->hbm_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. + * @cb_pos: callback block. + * @cl: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _mei_irq_thread_read(struct mei_device *dev,	s32 *slots, +			struct mei_cl_cb *cb_pos, +			struct mei_cl *cl, +			struct mei_io_list *cmpl_list) +{ +	if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_flow_control))) { +		/* return the cancel routine */ +		list_del(&cb_pos->cb_list); +		return -EBADMSG; +	} + +	*slots -= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_flow_control) + 3) / 4; +	if (mei_send_flow_control(dev, cl)) { +		cl->status = -ENODEV; +		cb_pos->information = 0; +		list_move_tail(&cb_pos->cb_list, &cmpl_list->mei_cb.cb_list); +		return -ENODEV; +	} +	list_move_tail(&cb_pos->cb_list, &dev->read_list.mei_cb.cb_list); + +	return 0; +} + + +/** + * _mei_irq_thread_ioctl - processes ioctl related operation. + * + * @dev: the device structure. + * @slots: free slots. + * @cb_pos: callback block. + * @cl: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, +			struct mei_cl_cb *cb_pos, +			struct mei_cl *cl, +			struct mei_io_list *cmpl_list) +{ +	if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_client_connect_request))) { +		cl->state = MEI_FILE_CONNECTING; +		*slots -= (sizeof(struct mei_msg_hdr) + +			sizeof(struct hbm_client_connect_request) + 3) / 4; +		if (mei_connect(dev, cl)) { +			cl->status = -ENODEV; +			cb_pos->information = 0; +			list_del(&cb_pos->cb_list); +			return -ENODEV; +		} else { +			list_move_tail(&cb_pos->cb_list, +				&dev->ctrl_rd_list.mei_cb.cb_list); +			cl->timer_count = MEI_CONNECT_TIMEOUT; +		} +	} else { +		/* return the cancel routine */ +		list_del(&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. + * @cb_pos: callback block. + * @cl: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _mei_irq_thread_cmpl(struct mei_device *dev,	s32 *slots, +			struct mei_cl_cb *cb_pos, +			struct mei_cl *cl, +			struct mei_io_list *cmpl_list) +{ +	struct mei_msg_hdr *mei_hdr; + +	if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + +			(cb_pos->request_buffer.size - +			cb_pos->information))) { +		mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; +		mei_hdr->host_addr = cl->host_client_id; +		mei_hdr->me_addr = cl->me_client_id; +		mei_hdr->length = cb_pos->request_buffer.size - +					cb_pos->information; +		mei_hdr->msg_complete = 1; +		mei_hdr->reserved = 0; +		dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d" +			"mei_hdr->msg_complete = %d\n", +				cb_pos->request_buffer.size, +				mei_hdr->msg_complete); +		dev_dbg(&dev->pdev->dev, "cb_pos->information  =%lu\n", +				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 *) +				(cb_pos->request_buffer.data + +				cb_pos->information), +				mei_hdr->length)) { +			cl->status = -ENODEV; +			list_move_tail(&cb_pos->cb_list, +				&cmpl_list->mei_cb.cb_list); +			return -ENODEV; +		} else { +			if (mei_flow_ctrl_reduce(dev, cl)) +				return -ENODEV; +			cl->status = 0; +			cb_pos->information += mei_hdr->length; +			list_move_tail(&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 = cl->host_client_id; +		mei_hdr->me_addr = cl->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 *) +					(cb_pos->request_buffer.data + +					cb_pos->information), +					mei_hdr->length)) { +			cl->status = -ENODEV; +			list_move_tail(&cb_pos->cb_list, +				&cmpl_list->mei_cb.cb_list); +			return -ENODEV; +		} else { +			cb_pos->information += mei_hdr->length; +			dev_dbg(&dev->pdev->dev, +					"cb_pos->request_buffer.size =%d" +					" mei_hdr->msg_complete = %d\n", +					cb_pos->request_buffer.size, +					mei_hdr->msg_complete); +			dev_dbg(&dev->pdev->dev, "cb_pos->information  =%lu\n", +					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. + * @cb_pos: callback block. + * @cl: private data of the file object. + * @cmpl_list: complete list. + * + * returns 0, OK; otherwise, error. + */ +static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots, +			struct mei_cl_cb *cb_pos, +			struct mei_cl *cl, +			struct mei_io_list *cmpl_list) +{ +	struct mei_msg_hdr *mei_hdr; + +	if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + +			dev->iamthif_msg_buf_size - +			dev->iamthif_msg_buf_index)) { +		mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; +		mei_hdr->host_addr = cl->host_client_id; +		mei_hdr->me_addr = cl->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; +			cl->status = -ENODEV; +			list_del(&cb_pos->cb_list); +			return -ENODEV; +		} else { +			if (mei_flow_ctrl_reduce(dev, cl)) +				return -ENODEV; +			dev->iamthif_msg_buf_index += mei_hdr->length; +			cb_pos->information = dev->iamthif_msg_buf_index; +			cl->status = 0; +			dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; +			dev->iamthif_flow_control_pending = true; +			/* save iamthif cb sent to amthi client */ +			dev->iamthif_current_cb = cb_pos; +			list_move_tail(&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 = cl->host_client_id; +		mei_hdr->me_addr = cl->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)) { +			cl->status = -ENODEV; +			list_del(&cb_pos->cb_list); +		} else { +			dev->iamthif_msg_buf_index += mei_hdr->length; +		} +		return -EMSGSIZE; +	} else { +		return -EBADMSG; +	} + +	return 0; +} + +/** + * mei_irq_thread_read_handler - bottom half read routine after ISR to + * handle the read processing. + * + * @cmpl_list: An instance of our list structure + * @dev: the device structure + * @slots: slots to read. + * + * returns 0 on success, <0 on failure. + */ +static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list, +		struct mei_device *dev, +		s32 *slots) +{ +	struct mei_msg_hdr *mei_hdr; +	struct mei_cl *cl_pos = NULL; +	struct mei_cl *cl_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(cl_pos, cl_next, +					&dev->file_list, link) { +			dev_dbg(&dev->pdev->dev, +					"list_for_each_entry_safe read host" +					" client = %d, ME client = %d\n", +					cl_pos->host_client_id, +					cl_pos->me_client_id); +			if (cl_pos->host_client_id == mei_hdr->host_addr && +			    cl_pos->me_client_id == mei_hdr->me_addr) +				break; +		} + +		if (&cl_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_cl.host_client_id && +		   (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && +		   (dev->iamthif_state == MEI_IAMTHIF_READING)) { +		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); +		dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", +				mei_hdr->length); +		ret = mei_irq_thread_read_amthi_message(cmpl_list, +							dev, mei_hdr); +		if (ret) +			goto end; + +	} else { +		dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); +		ret = mei_irq_thread_read_client_message(cmpl_list, +							 dev, mei_hdr); +		if (ret) +			goto end; + +	} + +	/* reset the number of slots and header */ +	*slots = mei_count_full_read_slots(dev); +	dev->rd_msg_hdr = 0; + +	if (*slots == -EOVERFLOW) { +		/* overflow - reset */ +		dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); +		/* set the event since message has been read */ +		ret = -ERANGE; +		goto end; +	} +end: +	return ret; +} + + +/** + * mei_irq_thread_write_handler - bottom half write routine after + * ISR to handle the write processing. + * + * @cmpl_list: An instance of our list structure + * @dev: the device structure + * @slots: slots to write. + * + * returns 0 on success, <0 on failure. + */ +static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, +		struct mei_device *dev, +		s32 *slots) +{ + +	struct mei_cl *cl; +	struct mei_cl_cb *pos = NULL, *next = NULL; +	struct mei_io_list *list; +	int ret; + +	if (!mei_host_buffer_is_empty(dev)) { +		dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); +		return 0; +	} +	*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; +	list_for_each_entry_safe(pos, next, +			&list->mei_cb.cb_list, cb_list) { +		cl = (struct mei_cl *)pos->file_private; +		if (cl == NULL) +			continue; + +		cl->status = 0; +		list_del(&pos->cb_list); +		if (MEI_WRITING == cl->writing_state && +		   (pos->major_file_operations == MEI_WRITE) && +		   (cl != &dev->iamthif_cl)) { +			dev_dbg(&dev->pdev->dev, +				"MEI WRITE COMPLETE\n"); +			cl->writing_state = MEI_WRITE_COMPLETE; +			list_add_tail(&pos->cb_list, +				&cmpl_list->mei_cb.cb_list); +		} +		if (cl == &dev->iamthif_cl) { +			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 = true; +		wake_up_interruptible(&dev->wait_stop_wd); +		return 0; +	} + +	if (dev->extra_write_index) { +		dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n", +				dev->extra_write_index); +		mei_write_message(dev, +				(struct mei_msg_hdr *) &dev->ext_msg_buf[0], +				(unsigned char *) &dev->ext_msg_buf[1], +				(dev->extra_write_index - 1) * sizeof(u32)); +		*slots -= dev->extra_write_index; +		dev->extra_write_index = 0; +	} +	if (dev->mei_state == MEI_ENABLED) { +		if (dev->wd_pending && +			mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { +			if (mei_wd_send(dev)) +				dev_dbg(&dev->pdev->dev, "wd send failed.\n"); +			else +				if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) +					return -ENODEV; + +			dev->wd_pending = false; + +			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 */ +	dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); +	list_for_each_entry_safe(pos, next, +				&dev->ctrl_wr_list.mei_cb.cb_list, cb_list) { +		cl = (struct mei_cl *) pos->file_private; +		if (!cl) { +			list_del(&pos->cb_list); +			return -ENODEV; +		} +		switch (pos->major_file_operations) { +		case MEI_CLOSE: +			/* send disconnect message */ +			ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list); +			if (ret) +				return ret; + +			break; +		case MEI_READ: +			/* send flow control message */ +			ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list); +			if (ret) +				return ret; + +			break; +		case MEI_IOCTL: +			/* connect message */ +			if (mei_other_client_is_connecting(dev, cl)) +				continue; +			ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list); +			if (ret) +				return ret; + +			break; + +		default: +			BUG(); +		} + +	} +	/* complete  write list CB */ +	dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); +	list_for_each_entry_safe(pos, next, +			&dev->write_list.mei_cb.cb_list, cb_list) { +		cl = (struct mei_cl *)pos->file_private; +		if (cl == NULL) +			continue; + +		if (cl != &dev->iamthif_cl) { +			if (!mei_flow_ctrl_creds(dev, cl)) { +				dev_dbg(&dev->pdev->dev, +					"No flow control" +				    " credentials for client" +				    " %d, not sending.\n", +				    cl->host_client_id); +				continue; +			} +			ret = _mei_irq_thread_cmpl(dev, slots, +					    pos, +					    cl, cmpl_list); +			if (ret) +				return ret; + +		} else if (cl == &dev->iamthif_cl) { +			/* IAMTHIF IOCTL */ +			dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n"); +			if (!mei_flow_ctrl_creds(dev, cl)) { +				dev_dbg(&dev->pdev->dev, +					"No flow control" +				    " credentials for amthi" +				    " client %d.\n", +				    cl->host_client_id); +				continue; +			} +			ret = _mei_irq_thread_cmpl_iamthif(dev, +						slots, +						pos, +						cl, +						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_timer(struct work_struct *work) +{ +	unsigned long timeout; +	struct mei_cl *cl_pos = NULL; +	struct mei_cl *cl_next = NULL; +	struct list_head *amthi_complete_list = NULL; +	struct mei_cl_cb  *cb_pos = NULL; +	struct mei_cl_cb  *cb_next = NULL; + +	struct mei_device *dev = container_of(work, +					struct mei_device, timer_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(cl_pos, cl_next, &dev->file_list, link) { +		if (cl_pos->timer_count) { +			if (--cl_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->iamthif_stall_timer) { +		if (--dev->iamthif_stall_timer == 0) { +			dev_dbg(&dev->pdev->dev, "resetting 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 = false; +			dev->iamthif_ioctl = true; +			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; +			mei_run_next_iamthif_cmd(dev); +		} +	} + +	if (dev->iamthif_timer) { + +		timeout = dev->iamthif_timer + +				msecs_to_jiffies(IAMTHIF_READ_TIMER); + +		dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", +				dev->iamthif_timer); +		dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); +		dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); +		if (time_after(jiffies, timeout)) { +			/* +			 * User didn't read the AMTHI data on time (15sec) +			 * freeing AMTHI for other requests +			 */ + +			dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); + +			amthi_complete_list = &dev->amthi_read_complete_list. +					mei_cb.cb_list; + +			list_for_each_entry_safe(cb_pos, cb_next, amthi_complete_list, cb_list) { + +				cl_pos = cb_pos->file_object->private_data; + +				/* Finding the AMTHI entry. */ +				if (cl_pos == &dev->iamthif_cl) +					list_del(&cb_pos->cb_list); +			} +			if (dev->iamthif_current_cb) +				mei_free_cb_private(dev->iamthif_current_cb); + +			dev->iamthif_file_object->private_data = NULL; +			dev->iamthif_file_object = NULL; +			dev->iamthif_current_cb = NULL; +			dev->iamthif_timer = 0; +			mei_run_next_iamthif_cmd(dev); + +		} +	} +out: +	schedule_delayed_work(&dev->timer_work, 2 * HZ); +	mutex_unlock(&dev->device_lock); +} + +/** + *  mei_interrupt_thread_handler - function called after ISR to handle the interrupt + * processing. + * + * @irq: The irq number + * @dev_id: pointer to the device structure + * + * returns irqreturn_t + * + */ +irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) +{ +	struct mei_device *dev = (struct mei_device *) dev_id; +	struct mei_io_list complete_list; +	struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; +	struct mei_cl *cl; +	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_io_list_init(&complete_list); +	dev->host_hw_state = mei_hcsr_read(dev); + +	/* Ack the interrupt here +	 * In case of MSI we don't go through the quick handler */ +	if (pci_dev_msi_enabled(dev->pdev)) +		mei_reg_write(dev, H_CSR, dev->host_hw_state); + +	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. +			 */ +			mei_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 available 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 (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) { +		cl = (struct mei_cl *)cb_pos->file_private; +		list_del(&cb_pos->cb_list); +		if (cl) { +			if (cl != &dev->iamthif_cl) { +				dev_dbg(&dev->pdev->dev, "completing call back.\n"); +				_mei_cmpl(cl, cb_pos); +				cb_pos = NULL; +			} else if (cl == &dev->iamthif_cl) { +				_mei_cmpl_iamthif(dev, cb_pos); +			} +		} +	} +	return IRQ_HANDLED; +}  |