blob: 9ff8c61fe457a1ddf533c9c969894665b48e6362 [file] [log] [blame]
Oren Weilfb7d8792011-05-15 13:43:42 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
Tomas Winkler733ba912012-02-09 19:25:53 +02004 * Copyright (c) 2003-2012, Intel Corporation.
Oren Weilfb7d8792011-05-15 13:43:42 +03005 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 */
16
17
Tomas Winkler40e0b672013-03-27 16:58:30 +020018#include <linux/export.h>
Oren Weilfb7d8792011-05-15 13:43:42 +030019#include <linux/pci.h>
20#include <linux/kthread.h>
21#include <linux/interrupt.h>
22#include <linux/fs.h>
23#include <linux/jiffies.h>
24
Tomas Winkler4f3afe12012-05-09 16:38:59 +030025#include <linux/mei.h>
Tomas Winkler47a73802012-12-25 19:06:03 +020026
27#include "mei_dev.h"
Tomas Winkler0edb23f2013-01-08 23:07:12 +020028#include "hbm.h"
Tomas Winkler9dc64d62013-01-08 23:07:17 +020029#include "hw-me.h"
Tomas Winkler90e0b5f2013-01-08 23:07:14 +020030#include "client.h"
Oren Weilfb7d8792011-05-15 13:43:42 +030031
32
33/**
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020034 * mei_cl_complete_handler - processes completed operation for a client
Oren Weilfb7d8792011-05-15 13:43:42 +030035 *
36 * @cl: private data of the file object.
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020037 * @cb: callback block.
Oren Weilfb7d8792011-05-15 13:43:42 +030038 */
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020039static void mei_cl_complete_handler(struct mei_cl *cl, struct mei_cl_cb *cb)
Oren Weilfb7d8792011-05-15 13:43:42 +030040{
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020041 if (cb->fop_type == MEI_FOP_WRITE) {
42 mei_io_cb_free(cb);
43 cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +030044 cl->writing_state = MEI_WRITE_COMPLETE;
45 if (waitqueue_active(&cl->tx_wait))
46 wake_up_interruptible(&cl->tx_wait);
47
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020048 } else if (cb->fop_type == MEI_FOP_READ &&
Oren Weilfb7d8792011-05-15 13:43:42 +030049 MEI_READING == cl->reading_state) {
50 cl->reading_state = MEI_READ_COMPLETE;
51 if (waitqueue_active(&cl->rx_wait))
52 wake_up_interruptible(&cl->rx_wait);
Samuel Ortizcf3baef2013-03-27 17:29:57 +020053 else
54 mei_cl_bus_rx_event(cl);
Oren Weilfb7d8792011-05-15 13:43:42 +030055
56 }
57}
58
59/**
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020060 * mei_irq_compl_handler - dispatch complete handelers
61 * for the completed callbacks
62 *
63 * @dev - mei device
64 * @compl_list - list of completed cbs
65 */
66void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list)
67{
68 struct mei_cl_cb *cb, *next;
69 struct mei_cl *cl;
70
71 list_for_each_entry_safe(cb, next, &compl_list->list, list) {
72 cl = cb->cl;
73 list_del(&cb->list);
74 if (!cl)
75 continue;
76
77 dev_dbg(&dev->pdev->dev, "completing call back.\n");
78 if (cl == &dev->iamthif_cl)
79 mei_amthif_complete(dev, cb);
80 else
81 mei_cl_complete_handler(cl, cb);
82 }
83}
Tomas Winkler40e0b672013-03-27 16:58:30 +020084EXPORT_SYMBOL_GPL(mei_irq_compl_handler);
Tomas Winkler4c6e22b2013-03-17 11:41:20 +020085/**
Oren Weilfb7d8792011-05-15 13:43:42 +030086 * _mei_irq_thread_state_ok - checks if mei header matches file private data
87 *
88 * @cl: private data of the file object
89 * @mei_hdr: header of mei client message
90 *
91 * returns !=0 if matches, 0 if no match.
92 */
93static int _mei_irq_thread_state_ok(struct mei_cl *cl,
94 struct mei_msg_hdr *mei_hdr)
95{
96 return (cl->host_client_id == mei_hdr->host_addr &&
97 cl->me_client_id == mei_hdr->me_addr &&
98 cl->state == MEI_FILE_CONNECTED &&
99 MEI_READ_COMPLETE != cl->reading_state);
100}
101
102/**
103 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
104 * handle the read mei client message data processing.
105 *
106 * @complete_list: An instance of our list structure
107 * @dev: the device structure
108 * @mei_hdr: header of mei client message
109 *
110 * returns 0 on success, <0 on failure.
111 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200112static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300113 struct mei_device *dev,
114 struct mei_msg_hdr *mei_hdr)
115{
116 struct mei_cl *cl;
117 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +0300118 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300119
120 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200121 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300122 goto quit;
123
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200124 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200125 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300126 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
127 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200128 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300129
130 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200131 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300132 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200133 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300134 return -ENOMEM;
135 }
136 if (buffer)
137 mei_read_slots(dev, buffer, mei_hdr->length);
138
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200139 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300140 if (mei_hdr->msg_complete) {
141 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200142 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300143 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300144 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300145 cl->host_client_id,
146 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200147 cb_pos->buf_idx);
148
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200149 list_add_tail(&cb_pos->list,
150 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300151 }
152
153 break;
154 }
155
156 }
157
158quit:
159 dev_dbg(&dev->pdev->dev, "message read\n");
160 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200161 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200162 dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n",
163 MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300164 }
165
166 return 0;
167}
168
169/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300170 * _mei_irq_thread_close - processes close related operation.
171 *
172 * @dev: the device structure.
173 * @slots: free slots.
174 * @cb_pos: callback block.
175 * @cl: private data of the file object.
176 * @cmpl_list: complete list.
177 *
178 * returns 0, OK; otherwise, error.
179 */
180static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
181 struct mei_cl_cb *cb_pos,
182 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200183 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300184{
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200185 u32 msg_slots =
186 mei_data2slots(sizeof(struct hbm_client_connect_request));
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300187
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200188 if (*slots < msg_slots)
189 return -EMSGSIZE;
190
191 *slots -= msg_slots;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300192
Tomas Winkler8120e722012-12-25 19:06:11 +0200193 if (mei_hbm_cl_disconnect_req(dev, cl)) {
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300194 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200195 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200196 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200197 return -EIO;
Oren Weilfb7d8792011-05-15 13:43:42 +0300198 }
199
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200200 cl->state = MEI_FILE_DISCONNECTING;
201 cl->status = 0;
202 cb_pos->buf_idx = 0;
203 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
204 cl->timer_count = MEI_CONNECT_TIMEOUT;
205
Oren Weilfb7d8792011-05-15 13:43:42 +0300206 return 0;
207}
208
Oren Weilfb7d8792011-05-15 13:43:42 +0300209
210/**
211 * _mei_hb_read - processes read related operation.
212 *
213 * @dev: the device structure.
214 * @slots: free slots.
215 * @cb_pos: callback block.
216 * @cl: private data of the file object.
217 * @cmpl_list: complete list.
218 *
219 * returns 0, OK; otherwise, error.
220 */
221static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
222 struct mei_cl_cb *cb_pos,
223 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200224 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300225{
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200226 u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control));
227
228 if (*slots < msg_slots) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300229 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200230 list_del(&cb_pos->list);
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200231 return -EMSGSIZE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300232 }
233
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200234 *slots -= msg_slots;
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300235
Tomas Winkler8120e722012-12-25 19:06:11 +0200236 if (mei_hbm_cl_flow_control_req(dev, cl)) {
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200237 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200238 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200239 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200240 return -ENODEV;
241 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200242 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200243
Oren Weilfb7d8792011-05-15 13:43:42 +0300244 return 0;
245}
246
247
248/**
249 * _mei_irq_thread_ioctl - processes ioctl related operation.
250 *
251 * @dev: the device structure.
252 * @slots: free slots.
253 * @cb_pos: callback block.
254 * @cl: private data of the file object.
255 * @cmpl_list: complete list.
256 *
257 * returns 0, OK; otherwise, error.
258 */
259static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
260 struct mei_cl_cb *cb_pos,
261 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200262 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300263{
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200264 u32 msg_slots =
265 mei_data2slots(sizeof(struct hbm_client_connect_request));
266
267 if (*slots < msg_slots) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300268 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200269 list_del(&cb_pos->list);
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200270 return -EMSGSIZE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300271 }
272
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200273 *slots -= msg_slots;
274
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300275 cl->state = MEI_FILE_CONNECTING;
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200276
Tomas Winkler8120e722012-12-25 19:06:11 +0200277 if (mei_hbm_cl_connect_req(dev, cl)) {
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300278 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200279 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200280 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300281 return -ENODEV;
282 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200283 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300284 cl->timer_count = MEI_CONNECT_TIMEOUT;
285 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300286 return 0;
287}
288
289/**
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200290 * mei_irq_thread_write_complete - write messages to device.
Oren Weilfb7d8792011-05-15 13:43:42 +0300291 *
292 * @dev: the device structure.
293 * @slots: free slots.
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200294 * @cb: callback block.
Oren Weilfb7d8792011-05-15 13:43:42 +0300295 * @cmpl_list: complete list.
296 *
297 * returns 0, OK; otherwise, error.
298 */
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200299static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots,
300 struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300301{
Tomas Winklere46f1872012-12-25 19:06:10 +0200302 struct mei_msg_hdr mei_hdr;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200303 struct mei_cl *cl = cb->cl;
304 size_t len = cb->request_buffer.size - cb->buf_idx;
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200305 u32 msg_slots = mei_data2slots(len);
Oren Weilfb7d8792011-05-15 13:43:42 +0300306
Tomas Winklere46f1872012-12-25 19:06:10 +0200307 mei_hdr.host_addr = cl->host_client_id;
308 mei_hdr.me_addr = cl->me_client_id;
309 mei_hdr.reserved = 0;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200310
311 if (*slots >= msg_slots) {
Tomas Winklere46f1872012-12-25 19:06:10 +0200312 mei_hdr.length = len;
313 mei_hdr.msg_complete = 1;
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200314 /* Split the message only if we can write the whole host buffer */
Tomas Winkler24aadc82012-06-25 23:46:27 +0300315 } else if (*slots == dev->hbuf_depth) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200316 msg_slots = *slots;
317 len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
Tomas Winklere46f1872012-12-25 19:06:10 +0200318 mei_hdr.length = len;
319 mei_hdr.msg_complete = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300320 } else {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200321 /* wait for next time the host buffer is empty */
322 return 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300323 }
324
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200325 dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n",
326 cb->request_buffer.size, cb->buf_idx);
Tomas Winklere46f1872012-12-25 19:06:10 +0200327 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr));
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200328
329 *slots -= msg_slots;
Tomas Winklere46f1872012-12-25 19:06:10 +0200330 if (mei_write_message(dev, &mei_hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200331 cb->request_buffer.data + cb->buf_idx)) {
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200332 cl->status = -ENODEV;
333 list_move_tail(&cb->list, &cmpl_list->list);
334 return -ENODEV;
335 }
336
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200337 if (mei_cl_flow_ctrl_reduce(cl))
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200338 return -ENODEV;
339
340 cl->status = 0;
Tomas Winklere46f1872012-12-25 19:06:10 +0200341 cb->buf_idx += mei_hdr.length;
342 if (mei_hdr.msg_complete)
Tomas Winklerea3b5fb2012-11-18 15:13:16 +0200343 list_move_tail(&cb->list, &dev->write_waiting_list.list);
344
Oren Weilfb7d8792011-05-15 13:43:42 +0300345 return 0;
346}
347
348/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300349 * mei_irq_thread_read_handler - bottom half read routine after ISR to
350 * handle the read processing.
351 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300352 * @dev: the device structure
Tomas Winkler06ecd642013-02-06 14:06:42 +0200353 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300354 * @slots: slots to read.
355 *
356 * returns 0 on success, <0 on failure.
357 */
Tomas Winkler06ecd642013-02-06 14:06:42 +0200358int mei_irq_read_handler(struct mei_device *dev,
359 struct mei_cl_cb *cmpl_list, s32 *slots)
Oren Weilfb7d8792011-05-15 13:43:42 +0300360{
361 struct mei_msg_hdr *mei_hdr;
362 struct mei_cl *cl_pos = NULL;
363 struct mei_cl *cl_next = NULL;
364 int ret = 0;
365
366 if (!dev->rd_msg_hdr) {
Tomas Winkler827eef52013-02-06 14:06:41 +0200367 dev->rd_msg_hdr = mei_read_hdr(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300368 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
369 (*slots)--;
370 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
371 }
372 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200373 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Oren Weilfb7d8792011-05-15 13:43:42 +0300374
375 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
376 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
377 ret = -EBADMSG;
378 goto end;
379 }
380
381 if (mei_hdr->host_addr || mei_hdr->me_addr) {
382 list_for_each_entry_safe(cl_pos, cl_next,
383 &dev->file_list, link) {
384 dev_dbg(&dev->pdev->dev,
385 "list_for_each_entry_safe read host"
386 " client = %d, ME client = %d\n",
387 cl_pos->host_client_id,
388 cl_pos->me_client_id);
389 if (cl_pos->host_client_id == mei_hdr->host_addr &&
390 cl_pos->me_client_id == mei_hdr->me_addr)
391 break;
392 }
393
394 if (&cl_pos->link == &dev->file_list) {
395 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
396 ret = -EBADMSG;
397 goto end;
398 }
399 }
400 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
401 dev_dbg(&dev->pdev->dev,
402 "we can't read the message slots =%08x.\n",
403 *slots);
404 /* we can't read the message */
405 ret = -ERANGE;
406 goto end;
407 }
408
409 /* decide where to read the message too */
410 if (!mei_hdr->host_addr) {
411 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
Tomas Winklerbb1b0132012-12-25 19:06:07 +0200412 mei_hbm_dispatch(dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300413 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
414 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
415 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
416 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
417 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
Tomas Winkler15d4acc2012-12-25 19:06:00 +0200418
419 dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
Tomas Winkler19838fb2012-11-01 21:17:15 +0200420
421 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300422 if (ret)
423 goto end;
Oren Weilfb7d8792011-05-15 13:43:42 +0300424 } else {
425 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
426 ret = mei_irq_thread_read_client_message(cmpl_list,
427 dev, mei_hdr);
428 if (ret)
429 goto end;
430
431 }
432
433 /* reset the number of slots and header */
434 *slots = mei_count_full_read_slots(dev);
435 dev->rd_msg_hdr = 0;
436
437 if (*slots == -EOVERFLOW) {
438 /* overflow - reset */
439 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
440 /* set the event since message has been read */
441 ret = -ERANGE;
442 goto end;
443 }
444end:
445 return ret;
446}
Tomas Winkler40e0b672013-03-27 16:58:30 +0200447EXPORT_SYMBOL_GPL(mei_irq_read_handler);
Oren Weilfb7d8792011-05-15 13:43:42 +0300448
449
450/**
Tomas Winkler06ecd642013-02-06 14:06:42 +0200451 * mei_irq_write_handler - dispatch write requests
452 * after irq received
Oren Weilfb7d8792011-05-15 13:43:42 +0300453 *
Oren Weilfb7d8792011-05-15 13:43:42 +0300454 * @dev: the device structure
Tomas Winkler9a84d612012-11-18 15:13:18 +0200455 * @cmpl_list: An instance of our list structure
Oren Weilfb7d8792011-05-15 13:43:42 +0300456 *
457 * returns 0 on success, <0 on failure.
458 */
Tomas Winklerc8c8d082013-03-11 18:27:02 +0200459int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300460{
461
462 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200463 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200464 struct mei_cl_cb *list;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200465 s32 slots;
Oren Weilfb7d8792011-05-15 13:43:42 +0300466 int ret;
467
Tomas Winkler827eef52013-02-06 14:06:41 +0200468 if (!mei_hbuf_is_ready(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300469 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
470 return 0;
471 }
Tomas Winkler9a84d612012-11-18 15:13:18 +0200472 slots = mei_hbuf_empty_slots(dev);
473 if (slots <= 0)
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300474 return -EMSGSIZE;
475
Oren Weilfb7d8792011-05-15 13:43:42 +0300476 /* complete all waiting for write CB */
477 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
478
479 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200480 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200481 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200482 if (cl == NULL)
483 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300484
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200485 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200486 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200487 if (MEI_WRITING == cl->writing_state &&
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200488 pos->fop_type == MEI_FOP_WRITE &&
489 cl != &dev->iamthif_cl) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300490 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200491 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200492 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200493 }
494 if (cl == &dev->iamthif_cl) {
495 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
496 if (dev->iamthif_flow_control_pending) {
Tomas Winkler9a84d612012-11-18 15:13:18 +0200497 ret = mei_amthif_irq_read(dev, &slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200498 if (ret)
499 return ret;
500 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300501 }
502 }
503
Tomas Winklerc216fde2012-08-16 19:39:43 +0300504 if (dev->wd_state == MEI_WD_STOPPING) {
505 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +0300506 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +0300507 }
508
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200509 if (dev->wr_ext_msg.hdr.length) {
510 mei_write_message(dev, &dev->wr_ext_msg.hdr,
Tomas Winkler438763f2012-12-25 19:05:59 +0200511 dev->wr_ext_msg.data);
Tomas Winkler9a84d612012-11-18 15:13:18 +0200512 slots -= mei_data2slots(dev->wr_ext_msg.hdr.length);
Tomas Winkler5fb54fb2012-11-18 15:13:15 +0200513 dev->wr_ext_msg.hdr.length = 0;
Oren Weilfb7d8792011-05-15 13:43:42 +0300514 }
Tomas Winklerb210d752012-08-07 00:03:56 +0300515 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300516 if (dev->wd_pending &&
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200517 mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300518 if (mei_wd_send(dev))
519 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200520 else if (mei_cl_flow_ctrl_reduce(&dev->wd_cl))
Tomas Winkler483136e2012-07-04 19:24:54 +0300521 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300522
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300523 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300524
Tomas Winklerc216fde2012-08-16 19:39:43 +0300525 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200526 slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300527 else
Tomas Winkler9a84d612012-11-18 15:13:18 +0200528 slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +0300529 }
530 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300531
532 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +0200533 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200534 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200535 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +0200536 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200537 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200538 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +0300539 }
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200540 switch (pos->fop_type) {
541 case MEI_FOP_CLOSE:
Tomas Winklerc8372092011-11-27 21:43:33 +0200542 /* send disconnect message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200543 ret = _mei_irq_thread_close(dev, &slots, pos,
544 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200545 if (ret)
546 return ret;
547
548 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200549 case MEI_FOP_READ:
Tomas Winklerc8372092011-11-27 21:43:33 +0200550 /* send flow control message */
Tomas Winkler9a84d612012-11-18 15:13:18 +0200551 ret = _mei_irq_thread_read(dev, &slots, pos,
552 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200553 if (ret)
554 return ret;
555
556 break;
Tomas Winkler4b8960b2012-11-11 17:38:00 +0200557 case MEI_FOP_IOCTL:
Tomas Winklerc8372092011-11-27 21:43:33 +0200558 /* connect message */
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200559 if (mei_cl_is_other_connecting(cl))
Tomas Winklerc8372092011-11-27 21:43:33 +0200560 continue;
Tomas Winkler9a84d612012-11-18 15:13:18 +0200561 ret = _mei_irq_thread_ioctl(dev, &slots, pos,
562 cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +0200563 if (ret)
564 return ret;
565
566 break;
567
568 default:
569 BUG();
570 }
571
Oren Weilfb7d8792011-05-15 13:43:42 +0300572 }
573 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200574 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200575 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200576 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200577 if (cl == NULL)
578 continue;
Tomas Winkler90e0b5f2013-01-08 23:07:14 +0200579 if (mei_cl_flow_ctrl_creds(cl) <= 0) {
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200580 dev_dbg(&dev->pdev->dev,
581 "No flow control credentials for client %d, not sending.\n",
582 cl->host_client_id);
583 continue;
584 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300585
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200586 if (cl == &dev->iamthif_cl)
Tomas Winkler9a84d612012-11-18 15:13:18 +0200587 ret = mei_amthif_irq_write_complete(dev, &slots,
Tomas Winkler24c656e2012-11-18 15:13:17 +0200588 pos, cmpl_list);
Tomas Winklerbe9d87a2012-11-18 15:13:19 +0200589 else
590 ret = mei_irq_thread_write_complete(dev, &slots, pos,
591 cmpl_list);
592 if (ret)
593 return ret;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200594
Oren Weilfb7d8792011-05-15 13:43:42 +0300595 }
596 return 0;
597}
Tomas Winkler40e0b672013-03-27 16:58:30 +0200598EXPORT_SYMBOL_GPL(mei_irq_write_handler);
Oren Weilfb7d8792011-05-15 13:43:42 +0300599
600
601
602/**
603 * mei_timer - timer function.
604 *
605 * @work: pointer to the work_struct structure
606 *
607 * NOTE: This function is called by timer interrupt work
608 */
Oren Weila61c6532011-09-07 09:03:13 +0300609void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +0300610{
611 unsigned long timeout;
612 struct mei_cl *cl_pos = NULL;
613 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300614 struct mei_cl_cb *cb_pos = NULL;
615 struct mei_cl_cb *cb_next = NULL;
616
617 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +0300618 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +0300619
620
621 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +0300622 if (dev->dev_state != MEI_DEV_ENABLED) {
623 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300624 if (dev->init_clients_timer) {
625 if (--dev->init_clients_timer == 0) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300626 dev_err(&dev->pdev->dev, "reset: init clients timeout ,init clients state = %d.\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300627 dev->init_clients_state);
628 mei_reset(dev, 1);
629 }
630 }
631 }
632 goto out;
633 }
634 /*** connect/disconnect timeouts ***/
635 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
636 if (cl_pos->timer_count) {
637 if (--cl_pos->timer_count == 0) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300638 dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300639 mei_reset(dev, 1);
640 goto out;
641 }
642 }
643 }
644
Oren Weilfb7d8792011-05-15 13:43:42 +0300645 if (dev->iamthif_stall_timer) {
646 if (--dev->iamthif_stall_timer == 0) {
Tomas Winklerd6c36a42013-04-08 21:56:38 +0300647 dev_err(&dev->pdev->dev, "reset: amthif hanged.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +0300648 mei_reset(dev, 1);
649 dev->iamthif_msg_buf_size = 0;
650 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300651 dev->iamthif_canceled = false;
652 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300653 dev->iamthif_state = MEI_IAMTHIF_IDLE;
654 dev->iamthif_timer = 0;
655
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200656 mei_io_cb_free(dev->iamthif_current_cb);
657 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300658
659 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200660 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300661 }
662 }
663
664 if (dev->iamthif_timer) {
665
666 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +0200667 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +0300668
669 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
670 dev->iamthif_timer);
671 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
672 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
673 if (time_after(jiffies, timeout)) {
674 /*
675 * User didn't read the AMTHI data on time (15sec)
676 * freeing AMTHI for other requests
677 */
678
679 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
680
Tomas Winklere773efc2012-11-11 17:37:58 +0200681 list_for_each_entry_safe(cb_pos, cb_next,
682 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300683
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200684 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +0300685
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200686 /* Finding the AMTHI entry. */
687 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200688 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300689 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +0200690 mei_io_cb_free(dev->iamthif_current_cb);
691 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300692
693 dev->iamthif_file_object->private_data = NULL;
694 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300695 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +0200696 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300697
698 }
699 }
700out:
Tomas Winkler441ab502011-12-13 23:39:34 +0200701 schedule_delayed_work(&dev->timer_work, 2 * HZ);
702 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +0300703}
704