blob: acc994e3f20b26f532d57f5ce8210d70cda598ab [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
18#include <linux/pci.h>
19#include <linux/kthread.h>
20#include <linux/interrupt.h>
21#include <linux/fs.h>
22#include <linux/jiffies.h>
23
24#include "mei_dev.h"
Tomas Winkler4f3afe12012-05-09 16:38:59 +030025#include <linux/mei.h>
Oren Weilfb7d8792011-05-15 13:43:42 +030026#include "hw.h"
27#include "interface.h"
28
29
30/**
31 * mei_interrupt_quick_handler - The ISR of the MEI device
32 *
33 * @irq: The irq number
34 * @dev_id: pointer to the device structure
35 *
36 * returns irqreturn_t
37 */
38irqreturn_t mei_interrupt_quick_handler(int irq, void *dev_id)
39{
40 struct mei_device *dev = (struct mei_device *) dev_id;
41 u32 csr_reg = mei_hcsr_read(dev);
42
43 if ((csr_reg & H_IS) != H_IS)
44 return IRQ_NONE;
45
46 /* clear H_IS bit in H_CSR */
47 mei_reg_write(dev, H_CSR, csr_reg);
48
49 return IRQ_WAKE_THREAD;
50}
51
52/**
53 * _mei_cmpl - processes completed operation.
54 *
55 * @cl: private data of the file object.
56 * @cb_pos: callback block.
57 */
58static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos)
59{
60 if (cb_pos->major_file_operations == MEI_WRITE) {
Tomas Winkler601a1ef2012-10-09 16:50:20 +020061 mei_io_cb_free(cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +030062 cb_pos = NULL;
63 cl->writing_state = MEI_WRITE_COMPLETE;
64 if (waitqueue_active(&cl->tx_wait))
65 wake_up_interruptible(&cl->tx_wait);
66
67 } else if (cb_pos->major_file_operations == MEI_READ &&
68 MEI_READING == cl->reading_state) {
69 cl->reading_state = MEI_READ_COMPLETE;
70 if (waitqueue_active(&cl->rx_wait))
71 wake_up_interruptible(&cl->rx_wait);
72
73 }
74}
75
76/**
Oren Weilfb7d8792011-05-15 13:43:42 +030077 * _mei_irq_thread_state_ok - checks if mei header matches file private data
78 *
79 * @cl: private data of the file object
80 * @mei_hdr: header of mei client message
81 *
82 * returns !=0 if matches, 0 if no match.
83 */
84static int _mei_irq_thread_state_ok(struct mei_cl *cl,
85 struct mei_msg_hdr *mei_hdr)
86{
87 return (cl->host_client_id == mei_hdr->host_addr &&
88 cl->me_client_id == mei_hdr->me_addr &&
89 cl->state == MEI_FILE_CONNECTED &&
90 MEI_READ_COMPLETE != cl->reading_state);
91}
92
93/**
94 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
95 * handle the read mei client message data processing.
96 *
97 * @complete_list: An instance of our list structure
98 * @dev: the device structure
99 * @mei_hdr: header of mei client message
100 *
101 * returns 0 on success, <0 on failure.
102 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200103static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300104 struct mei_device *dev,
105 struct mei_msg_hdr *mei_hdr)
106{
107 struct mei_cl *cl;
108 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +0300109 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300110
111 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200112 if (list_empty(&dev->read_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300113 goto quit;
114
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200115 list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200116 cl = cb_pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300117 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
118 cl->reading_state = MEI_READING;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200119 buffer = cb_pos->response_buffer.data + cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300120
121 if (cb_pos->response_buffer.size <
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200122 mei_hdr->length + cb_pos->buf_idx) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300123 dev_dbg(&dev->pdev->dev, "message overflow.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200124 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300125 return -ENOMEM;
126 }
127 if (buffer)
128 mei_read_slots(dev, buffer, mei_hdr->length);
129
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200130 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300131 if (mei_hdr->msg_complete) {
132 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200133 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300134 dev_dbg(&dev->pdev->dev,
Tomas Winklera4136b42012-09-11 00:43:22 +0300135 "completed read H cl = %d, ME cl = %d, length = %lu\n",
Oren Weilfb7d8792011-05-15 13:43:42 +0300136 cl->host_client_id,
137 cl->me_client_id,
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200138 cb_pos->buf_idx);
139
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200140 list_add_tail(&cb_pos->list,
141 &complete_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300142 }
143
144 break;
145 }
146
147 }
148
149quit:
150 dev_dbg(&dev->pdev->dev, "message read\n");
151 if (!buffer) {
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200152 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
Oren Weilfb7d8792011-05-15 13:43:42 +0300153 dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n",
154 *(u32 *) dev->rd_msg_buf);
155 }
156
157 return 0;
158}
159
160/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300161 * _mei_irq_thread_close - processes close related operation.
162 *
163 * @dev: the device structure.
164 * @slots: free slots.
165 * @cb_pos: callback block.
166 * @cl: private data of the file object.
167 * @cmpl_list: complete list.
168 *
169 * returns 0, OK; otherwise, error.
170 */
171static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
172 struct mei_cl_cb *cb_pos,
173 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200174 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300175{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300176 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
177 sizeof(struct hbm_client_disconnect_request)))
Oren Weilfb7d8792011-05-15 13:43:42 +0300178 return -EBADMSG;
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300179
180 *slots -= mei_data2slots(sizeof(struct hbm_client_disconnect_request));
181
182 if (mei_disconnect(dev, cl)) {
183 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200184 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200185 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300186 return -EMSGSIZE;
187 } else {
188 cl->state = MEI_FILE_DISCONNECTING;
189 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200190 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200191 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300192 cl->timer_count = MEI_CONNECT_TIMEOUT;
Oren Weilfb7d8792011-05-15 13:43:42 +0300193 }
194
195 return 0;
196}
197
198/**
199 * is_treat_specially_client - checks if the message belongs
200 * to the file private data.
201 *
202 * @cl: private data of the file object
203 * @rs: connect response bus message
204 *
205 */
206static bool is_treat_specially_client(struct mei_cl *cl,
207 struct hbm_client_connect_response *rs)
208{
209
210 if (cl->host_client_id == rs->host_addr &&
211 cl->me_client_id == rs->me_addr) {
212 if (!rs->status) {
213 cl->state = MEI_FILE_CONNECTED;
214 cl->status = 0;
215
216 } else {
217 cl->state = MEI_FILE_DISCONNECTED;
218 cl->status = -ENODEV;
219 }
220 cl->timer_count = 0;
221
222 return true;
223 }
224 return false;
225}
226
227/**
228 * mei_client_connect_response - connects to response irq routine
229 *
230 * @dev: the device structure
231 * @rs: connect response bus message
232 */
233static void mei_client_connect_response(struct mei_device *dev,
234 struct hbm_client_connect_response *rs)
235{
236
237 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200238 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300239
240 dev_dbg(&dev->pdev->dev,
241 "connect_response:\n"
242 "ME Client = %d\n"
243 "Host Client = %d\n"
244 "Status = %d\n",
245 rs->me_addr,
246 rs->host_addr,
247 rs->status);
248
249 /* if WD or iamthif client treat specially */
250
251 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300252 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Tomas Winkler70cd5332011-12-22 18:50:50 +0200253 mei_watchdog_register(dev);
Oren Weil9ce178e2011-09-07 09:03:09 +0300254
Tomas Winkler70cd5332011-12-22 18:50:50 +0200255 /* next step in the state maching */
Tomas Winkler19838fb2012-11-01 21:17:15 +0200256 mei_amthif_host_init(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300257 return;
258 }
259
260 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
261 dev->iamthif_state = MEI_IAMTHIF_IDLE;
262 return;
263 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200264 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200265
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200266 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200267 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200268 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200269 return;
270 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200271 if (MEI_IOCTL == pos->major_file_operations) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200272 if (is_treat_specially_client(cl, rs)) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200273 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200274 cl->status = 0;
275 cl->timer_count = 0;
276 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300277 }
278 }
279 }
280}
281
282/**
283 * mei_client_disconnect_response - disconnects from response irq routine
284 *
285 * @dev: the device structure
286 * @rs: disconnect response bus message
287 */
288static void mei_client_disconnect_response(struct mei_device *dev,
289 struct hbm_client_connect_response *rs)
290{
291 struct mei_cl *cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200292 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300293
294 dev_dbg(&dev->pdev->dev,
295 "disconnect_response:\n"
296 "ME Client = %d\n"
297 "Host Client = %d\n"
298 "Status = %d\n",
299 rs->me_addr,
300 rs->host_addr,
301 rs->status);
302
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200303 list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200304 cl = pos->cl;
Oren Weilfb7d8792011-05-15 13:43:42 +0300305
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200306 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200307 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200308 return;
309 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300310
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200311 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
312 if (cl->host_client_id == rs->host_addr &&
313 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300314
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200315 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200316 if (!rs->status)
317 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300318
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200319 cl->status = 0;
320 cl->timer_count = 0;
321 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300322 }
323 }
324}
325
326/**
327 * same_flow_addr - tells if they have the same address.
328 *
329 * @file: private data of the file object.
330 * @flow: flow control.
331 *
332 * returns !=0, same; 0,not.
333 */
334static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
335{
336 return (cl->host_client_id == flow->host_addr &&
337 cl->me_client_id == flow->me_addr);
338}
339
340/**
341 * add_single_flow_creds - adds single buffer credentials.
342 *
343 * @file: private data ot the file object.
344 * @flow: flow control.
345 */
346static void add_single_flow_creds(struct mei_device *dev,
347 struct hbm_flow_control *flow)
348{
349 struct mei_me_client *client;
350 int i;
351
Tomas Winklercf9673d2011-06-06 10:44:33 +0300352 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300353 client = &dev->me_clients[i];
354 if (client && flow->me_addr == client->client_id) {
355 if (client->props.single_recv_buf) {
356 client->mei_flow_ctrl_creds++;
357 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
358 flow->me_addr);
359 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
360 client->mei_flow_ctrl_creds);
361 } else {
362 BUG(); /* error in flow control */
363 }
364 }
365 }
366}
367
368/**
369 * mei_client_flow_control_response - flow control response irq routine
370 *
371 * @dev: the device structure
372 * @flow_control: flow control response bus message
373 */
374static void mei_client_flow_control_response(struct mei_device *dev,
375 struct hbm_flow_control *flow_control)
376{
377 struct mei_cl *cl_pos = NULL;
378 struct mei_cl *cl_next = NULL;
379
380 if (!flow_control->host_addr) {
381 /* single receive buffer */
382 add_single_flow_creds(dev, flow_control);
383 } else {
384 /* normal connection */
385 list_for_each_entry_safe(cl_pos, cl_next,
386 &dev->file_list, link) {
387 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
388
389 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
390 cl_pos->host_client_id,
391 cl_pos->me_client_id);
392 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
393 flow_control->host_addr,
394 flow_control->me_addr);
395 if (same_flow_addr(cl_pos, flow_control)) {
396 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
397 flow_control->host_addr,
398 flow_control->me_addr);
399 cl_pos->mei_flow_ctrl_creds++;
400 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
401 cl_pos->mei_flow_ctrl_creds);
402 break;
403 }
404 }
405 }
406}
407
408/**
409 * same_disconn_addr - tells if they have the same address
410 *
411 * @file: private data of the file object.
412 * @disconn: disconnection request.
413 *
414 * returns !=0, same; 0,not.
415 */
416static int same_disconn_addr(struct mei_cl *cl,
417 struct hbm_client_disconnect_request *disconn)
418{
419 return (cl->host_client_id == disconn->host_addr &&
420 cl->me_client_id == disconn->me_addr);
421}
422
423/**
424 * mei_client_disconnect_request - disconnects from request irq routine
425 *
426 * @dev: the device structure.
427 * @disconnect_req: disconnect request bus message.
428 */
429static void mei_client_disconnect_request(struct mei_device *dev,
430 struct hbm_client_disconnect_request *disconnect_req)
431{
432 struct mei_msg_hdr *mei_hdr;
433 struct hbm_client_connect_response *disconnect_res;
434 struct mei_cl *cl_pos = NULL;
435 struct mei_cl *cl_next = NULL;
436
437 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
438 if (same_disconn_addr(cl_pos, disconnect_req)) {
439 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
440 disconnect_req->host_addr,
441 disconnect_req->me_addr);
442 cl_pos->state = MEI_FILE_DISCONNECTED;
443 cl_pos->timer_count = 0;
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300444 if (cl_pos == &dev->wd_cl)
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300445 dev->wd_pending = false;
Tomas Winklerd242a0a2012-07-04 19:24:50 +0300446 else if (cl_pos == &dev->iamthif_cl)
Oren Weilfb7d8792011-05-15 13:43:42 +0300447 dev->iamthif_timer = 0;
448
449 /* prepare disconnect response */
450 mei_hdr =
451 (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
452 mei_hdr->host_addr = 0;
453 mei_hdr->me_addr = 0;
454 mei_hdr->length =
455 sizeof(struct hbm_client_connect_response);
456 mei_hdr->msg_complete = 1;
457 mei_hdr->reserved = 0;
458
459 disconnect_res =
460 (struct hbm_client_connect_response *)
461 &dev->ext_msg_buf[1];
462 disconnect_res->host_addr = cl_pos->host_client_id;
463 disconnect_res->me_addr = cl_pos->me_client_id;
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200464 disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300465 disconnect_res->status = 0;
466 dev->extra_write_index = 2;
467 break;
468 }
469 }
470}
471
472
473/**
474 * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
475 * handle the read bus message cmd processing.
476 *
477 * @dev: the device structure
478 * @mei_hdr: header of bus message
479 */
480static void mei_irq_thread_read_bus_message(struct mei_device *dev,
481 struct mei_msg_hdr *mei_hdr)
482{
483 struct mei_bus_message *mei_msg;
484 struct hbm_host_version_response *version_res;
485 struct hbm_client_connect_response *connect_res;
486 struct hbm_client_connect_response *disconnect_res;
487 struct hbm_flow_control *flow_control;
488 struct hbm_props_response *props_res;
489 struct hbm_host_enum_response *enum_res;
490 struct hbm_client_disconnect_request *disconnect_req;
491 struct hbm_host_stop_request *host_stop_req;
Oren Weilabc51b62011-09-21 16:45:30 +0300492 int res;
Oren Weilfb7d8792011-05-15 13:43:42 +0300493
Oren Weilfb7d8792011-05-15 13:43:42 +0300494
495 /* read the message to our buffer */
Oren Weilfb7d8792011-05-15 13:43:42 +0300496 BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf));
Tomas Winkleredf1eed2012-02-09 19:25:54 +0200497 mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length);
498 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
Oren Weilfb7d8792011-05-15 13:43:42 +0300499
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200500 switch (mei_msg->hbm_cmd) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300501 case HOST_START_RES_CMD:
502 version_res = (struct hbm_host_version_response *) mei_msg;
503 if (version_res->host_version_supported) {
504 dev->version.major_version = HBM_MAJOR_VERSION;
505 dev->version.minor_version = HBM_MINOR_VERSION;
Tomas Winklerb210d752012-08-07 00:03:56 +0300506 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300507 dev->init_clients_state == MEI_START_MESSAGE) {
508 dev->init_clients_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300509 mei_host_enum_clients_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300510 } else {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300511 dev->recvd_msg = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300512 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
513 mei_reset(dev, 1);
514 return;
515 }
516 } else {
517 dev->version = version_res->me_max_version;
518 /* send stop message */
Tomas Winkler97d5cb02012-03-06 22:34:32 +0200519 mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0];
Oren Weilfb7d8792011-05-15 13:43:42 +0300520 mei_hdr->host_addr = 0;
521 mei_hdr->me_addr = 0;
522 mei_hdr->length = sizeof(struct hbm_host_stop_request);
523 mei_hdr->msg_complete = 1;
524 mei_hdr->reserved = 0;
525
526 host_stop_req = (struct hbm_host_stop_request *)
527 &dev->wr_msg_buf[1];
528
529 memset(host_stop_req,
530 0,
531 sizeof(struct hbm_host_stop_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200532 host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300533 host_stop_req->reason = DRIVER_STOP_REQUEST;
534 mei_write_message(dev, mei_hdr,
535 (unsigned char *) (host_stop_req),
536 mei_hdr->length);
537 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
538 return;
539 }
540
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300541 dev->recvd_msg = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300542 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
543 break;
544
545 case CLIENT_CONNECT_RES_CMD:
546 connect_res =
547 (struct hbm_client_connect_response *) mei_msg;
548 mei_client_connect_response(dev, connect_res);
549 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
550 wake_up(&dev->wait_recvd_msg);
551 break;
552
553 case CLIENT_DISCONNECT_RES_CMD:
554 disconnect_res =
555 (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler441ab502011-12-13 23:39:34 +0200556 mei_client_disconnect_response(dev, disconnect_res);
Oren Weilfb7d8792011-05-15 13:43:42 +0300557 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
558 wake_up(&dev->wait_recvd_msg);
559 break;
560
561 case MEI_FLOW_CONTROL_CMD:
562 flow_control = (struct hbm_flow_control *) mei_msg;
563 mei_client_flow_control_response(dev, flow_control);
564 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
565 break;
566
567 case HOST_CLIENT_PROPERTIES_RES_CMD:
568 props_res = (struct hbm_props_response *)mei_msg;
569 if (props_res->status || !dev->me_clients) {
570 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
571 mei_reset(dev, 1);
572 return;
573 }
Tomas Winkler441ab502011-12-13 23:39:34 +0200574 if (dev->me_clients[dev->me_client_presentation_num]
Oren Weilfb7d8792011-05-15 13:43:42 +0300575 .client_id == props_res->address) {
576
577 dev->me_clients[dev->me_client_presentation_num].props
578 = props_res->client_properties;
579
Tomas Winklerb210d752012-08-07 00:03:56 +0300580 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300581 dev->init_clients_state ==
582 MEI_CLIENT_PROPERTIES_MESSAGE) {
583 dev->me_client_index++;
584 dev->me_client_presentation_num++;
Oren Weilabc51b62011-09-21 16:45:30 +0300585
Justin P. Mattock5f9092f32012-03-12 07:18:09 -0700586 /** Send Client Properties request **/
Oren Weilabc51b62011-09-21 16:45:30 +0300587 res = mei_host_client_properties(dev);
588 if (res < 0) {
589 dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed");
590 return;
591 } else if (!res) {
592 /*
593 * No more clients to send to.
594 * Clear Map for indicating now ME clients
595 * with associated host client
596 */
597 bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
598 dev->open_handle_count = 0;
599
600 /*
601 * Reserving the first three client IDs
602 * Client Id 0 - Reserved for MEI Bus Message communications
603 * Client Id 1 - Reserved for Watchdog
604 * Client ID 2 - Reserved for AMTHI
605 */
606 bitmap_set(dev->host_clients_map, 0, 3);
Tomas Winklerb210d752012-08-07 00:03:56 +0300607 dev->dev_state = MEI_DEV_ENABLED;
Oren Weilabc51b62011-09-21 16:45:30 +0300608
609 /* if wd initialization fails, initialization the AMTHI client,
610 * otherwise the AMTHI client will be initialized after the WD client connect response
611 * will be received
612 */
613 if (mei_wd_host_init(dev))
Tomas Winkler19838fb2012-11-01 21:17:15 +0200614 mei_amthif_host_init(dev);
Oren Weilabc51b62011-09-21 16:45:30 +0300615 }
616
Oren Weilfb7d8792011-05-15 13:43:42 +0300617 } else {
618 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message");
619 mei_reset(dev, 1);
620 return;
621 }
622 } else {
623 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n");
624 mei_reset(dev, 1);
625 return;
626 }
627 break;
628
629 case HOST_ENUM_RES_CMD:
630 enum_res = (struct hbm_host_enum_response *) mei_msg;
631 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
Tomas Winklerb210d752012-08-07 00:03:56 +0300632 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
Oren Weilfb7d8792011-05-15 13:43:42 +0300633 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
634 dev->init_clients_timer = 0;
635 dev->me_client_presentation_num = 0;
636 dev->me_client_index = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300637 mei_allocate_me_clients_storage(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300638 dev->init_clients_state =
639 MEI_CLIENT_PROPERTIES_MESSAGE;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300640 mei_host_client_properties(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300641 } else {
642 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
643 mei_reset(dev, 1);
644 return;
645 }
646 break;
647
648 case HOST_STOP_RES_CMD:
Tomas Winklerb210d752012-08-07 00:03:56 +0300649 dev->dev_state = MEI_DEV_DISABLED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300650 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
651 mei_reset(dev, 1);
652 break;
653
654 case CLIENT_DISCONNECT_REQ_CMD:
655 /* search for client */
656 disconnect_req =
657 (struct hbm_client_disconnect_request *) mei_msg;
658 mei_client_disconnect_request(dev, disconnect_req);
659 break;
660
661 case ME_STOP_REQ_CMD:
662 /* prepare stop request */
663 mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
664 mei_hdr->host_addr = 0;
665 mei_hdr->me_addr = 0;
666 mei_hdr->length = sizeof(struct hbm_host_stop_request);
667 mei_hdr->msg_complete = 1;
668 mei_hdr->reserved = 0;
669 host_stop_req =
670 (struct hbm_host_stop_request *) &dev->ext_msg_buf[1];
671 memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
Tomas Winkler1ca7e782012-02-26 23:18:57 +0200672 host_stop_req->hbm_cmd = HOST_STOP_REQ_CMD;
Oren Weilfb7d8792011-05-15 13:43:42 +0300673 host_stop_req->reason = DRIVER_STOP_REQUEST;
674 host_stop_req->reserved[0] = 0;
675 host_stop_req->reserved[1] = 0;
676 dev->extra_write_index = 2;
677 break;
678
679 default:
680 BUG();
681 break;
682
683 }
684}
685
686
687/**
688 * _mei_hb_read - processes read related operation.
689 *
690 * @dev: the device structure.
691 * @slots: free slots.
692 * @cb_pos: callback block.
693 * @cl: private data of the file object.
694 * @cmpl_list: complete list.
695 *
696 * returns 0, OK; otherwise, error.
697 */
698static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
699 struct mei_cl_cb *cb_pos,
700 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200701 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300702{
Tomas Winkler1e69d642012-05-29 16:39:12 +0300703 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300704 sizeof(struct hbm_flow_control))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300705 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200706 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300707 return -EBADMSG;
708 }
709
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300710 *slots -= mei_data2slots(sizeof(struct hbm_flow_control));
711
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200712 if (mei_send_flow_control(dev, cl)) {
713 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200714 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200715 list_move_tail(&cb_pos->list, &cmpl_list->list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200716 return -ENODEV;
717 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200718 list_move_tail(&cb_pos->list, &dev->read_list.list);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200719
Oren Weilfb7d8792011-05-15 13:43:42 +0300720 return 0;
721}
722
723
724/**
725 * _mei_irq_thread_ioctl - processes ioctl related operation.
726 *
727 * @dev: the device structure.
728 * @slots: free slots.
729 * @cb_pos: callback block.
730 * @cl: private data of the file object.
731 * @cmpl_list: complete list.
732 *
733 * returns 0, OK; otherwise, error.
734 */
735static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
736 struct mei_cl_cb *cb_pos,
737 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200738 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300739{
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300740 if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) +
Oren Weilfb7d8792011-05-15 13:43:42 +0300741 sizeof(struct hbm_client_connect_request))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300742 /* return the cancel routine */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200743 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300744 return -EBADMSG;
745 }
746
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300747 cl->state = MEI_FILE_CONNECTING;
748 *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request));
749 if (mei_connect(dev, cl)) {
750 cl->status = -ENODEV;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200751 cb_pos->buf_idx = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200752 list_del(&cb_pos->list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300753 return -ENODEV;
754 } else {
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200755 list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list);
Tomas Winklerb45f3cc2012-07-04 19:24:53 +0300756 cl->timer_count = MEI_CONNECT_TIMEOUT;
757 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300758 return 0;
759}
760
761/**
762 * _mei_irq_thread_cmpl - processes completed and no-iamthif operation.
763 *
764 * @dev: the device structure.
765 * @slots: free slots.
766 * @cb_pos: callback block.
767 * @cl: private data of the file object.
768 * @cmpl_list: complete list.
769 *
770 * returns 0, OK; otherwise, error.
771 */
772static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots,
773 struct mei_cl_cb *cb_pos,
774 struct mei_cl *cl,
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200775 struct mei_cl_cb *cmpl_list)
Oren Weilfb7d8792011-05-15 13:43:42 +0300776{
777 struct mei_msg_hdr *mei_hdr;
778
779 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200780 (cb_pos->request_buffer.size - cb_pos->buf_idx))) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300781 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
782 mei_hdr->host_addr = cl->host_client_id;
783 mei_hdr->me_addr = cl->me_client_id;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200784 mei_hdr->length = cb_pos->request_buffer.size - cb_pos->buf_idx;
Oren Weilfb7d8792011-05-15 13:43:42 +0300785 mei_hdr->msg_complete = 1;
786 mei_hdr->reserved = 0;
787 dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d"
788 "mei_hdr->msg_complete = %d\n",
789 cb_pos->request_buffer.size,
790 mei_hdr->msg_complete);
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200791 dev_dbg(&dev->pdev->dev, "cb_pos->buf_idx =%lu\n",
792 cb_pos->buf_idx);
Oren Weilfb7d8792011-05-15 13:43:42 +0300793 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
794 mei_hdr->length);
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300795 *slots -= mei_data2slots(mei_hdr->length);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200796 if (mei_write_message(dev, mei_hdr,
Oren Weilfb7d8792011-05-15 13:43:42 +0300797 (unsigned char *)
798 (cb_pos->request_buffer.data +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200799 cb_pos->buf_idx),
Oren Weilfb7d8792011-05-15 13:43:42 +0300800 mei_hdr->length)) {
801 cl->status = -ENODEV;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200802 list_move_tail(&cb_pos->list, &cmpl_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300803 return -ENODEV;
804 } else {
805 if (mei_flow_ctrl_reduce(dev, cl))
806 return -ENODEV;
807 cl->status = 0;
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200808 cb_pos->buf_idx += mei_hdr->length;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200809 list_move_tail(&cb_pos->list, &dev->write_waiting_list.list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300810 }
Tomas Winkler24aadc82012-06-25 23:46:27 +0300811 } else if (*slots == dev->hbuf_depth) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300812 /* buffer is still empty */
813 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
814 mei_hdr->host_addr = cl->host_client_id;
815 mei_hdr->me_addr = cl->me_client_id;
816 mei_hdr->length =
817 (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
818 mei_hdr->msg_complete = 0;
819 mei_hdr->reserved = 0;
Tomas Winkler7bdf72d2012-07-04 19:24:52 +0300820 *slots -= mei_data2slots(mei_hdr->length);
Tomas Winkler1ccb7b62012-03-14 14:39:42 +0200821 if (mei_write_message(dev, mei_hdr,
Oren Weilfb7d8792011-05-15 13:43:42 +0300822 (unsigned char *)
823 (cb_pos->request_buffer.data +
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200824 cb_pos->buf_idx),
Oren Weilfb7d8792011-05-15 13:43:42 +0300825 mei_hdr->length)) {
826 cl->status = -ENODEV;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200827 list_move_tail(&cb_pos->list, &cmpl_list->list);
Oren Weilfb7d8792011-05-15 13:43:42 +0300828 return -ENODEV;
829 } else {
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200830 cb_pos->buf_idx += mei_hdr->length;
Oren Weilfb7d8792011-05-15 13:43:42 +0300831 dev_dbg(&dev->pdev->dev,
832 "cb_pos->request_buffer.size =%d"
833 " mei_hdr->msg_complete = %d\n",
834 cb_pos->request_buffer.size,
835 mei_hdr->msg_complete);
Tomas Winklerebb108ef2012-10-09 16:50:16 +0200836 dev_dbg(&dev->pdev->dev, "cb_pos->buf_idx =%lu\n",
837 cb_pos->buf_idx);
Oren Weilfb7d8792011-05-15 13:43:42 +0300838 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
839 mei_hdr->length);
840 }
841 return -EMSGSIZE;
842 } else {
843 return -EBADMSG;
844 }
845
846 return 0;
847}
848
849/**
Oren Weilfb7d8792011-05-15 13:43:42 +0300850 * mei_irq_thread_read_handler - bottom half read routine after ISR to
851 * handle the read processing.
852 *
853 * @cmpl_list: An instance of our list structure
854 * @dev: the device structure
855 * @slots: slots to read.
856 *
857 * returns 0 on success, <0 on failure.
858 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200859static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list,
Oren Weilfb7d8792011-05-15 13:43:42 +0300860 struct mei_device *dev,
861 s32 *slots)
862{
863 struct mei_msg_hdr *mei_hdr;
864 struct mei_cl *cl_pos = NULL;
865 struct mei_cl *cl_next = NULL;
866 int ret = 0;
867
868 if (!dev->rd_msg_hdr) {
869 dev->rd_msg_hdr = mei_mecbrw_read(dev);
870 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
871 (*slots)--;
872 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
873 }
874 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
875 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length);
876
877 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
878 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
879 ret = -EBADMSG;
880 goto end;
881 }
882
883 if (mei_hdr->host_addr || mei_hdr->me_addr) {
884 list_for_each_entry_safe(cl_pos, cl_next,
885 &dev->file_list, link) {
886 dev_dbg(&dev->pdev->dev,
887 "list_for_each_entry_safe read host"
888 " client = %d, ME client = %d\n",
889 cl_pos->host_client_id,
890 cl_pos->me_client_id);
891 if (cl_pos->host_client_id == mei_hdr->host_addr &&
892 cl_pos->me_client_id == mei_hdr->me_addr)
893 break;
894 }
895
896 if (&cl_pos->link == &dev->file_list) {
897 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
898 ret = -EBADMSG;
899 goto end;
900 }
901 }
902 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
903 dev_dbg(&dev->pdev->dev,
904 "we can't read the message slots =%08x.\n",
905 *slots);
906 /* we can't read the message */
907 ret = -ERANGE;
908 goto end;
909 }
910
911 /* decide where to read the message too */
912 if (!mei_hdr->host_addr) {
913 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
914 mei_irq_thread_read_bus_message(dev, mei_hdr);
915 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
916 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
917 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
918 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
919 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
920 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
921 mei_hdr->length);
Tomas Winkler19838fb2012-11-01 21:17:15 +0200922
923 ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr);
Oren Weilfb7d8792011-05-15 13:43:42 +0300924 if (ret)
925 goto end;
926
927 } else {
928 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
929 ret = mei_irq_thread_read_client_message(cmpl_list,
930 dev, mei_hdr);
931 if (ret)
932 goto end;
933
934 }
935
936 /* reset the number of slots and header */
937 *slots = mei_count_full_read_slots(dev);
938 dev->rd_msg_hdr = 0;
939
940 if (*slots == -EOVERFLOW) {
941 /* overflow - reset */
942 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
943 /* set the event since message has been read */
944 ret = -ERANGE;
945 goto end;
946 }
947end:
948 return ret;
949}
950
951
952/**
953 * mei_irq_thread_write_handler - bottom half write routine after
954 * ISR to handle the write processing.
955 *
956 * @cmpl_list: An instance of our list structure
957 * @dev: the device structure
958 * @slots: slots to write.
959 *
960 * returns 0 on success, <0 on failure.
961 */
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200962static int mei_irq_thread_write_handler(struct mei_cl_cb *cmpl_list,
963 struct mei_device *dev, s32 *slots)
Oren Weilfb7d8792011-05-15 13:43:42 +0300964{
965
966 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200967 struct mei_cl_cb *pos = NULL, *next = NULL;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200968 struct mei_cl_cb *list;
Oren Weilfb7d8792011-05-15 13:43:42 +0300969 int ret;
970
Tomas Winkler726917f2012-06-25 23:46:28 +0300971 if (!mei_hbuf_is_empty(dev)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300972 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
973 return 0;
974 }
Tomas Winkler726917f2012-06-25 23:46:28 +0300975 *slots = mei_hbuf_empty_slots(dev);
Tomas Winkler7d5e0e52012-06-19 09:13:36 +0300976 if (*slots <= 0)
977 return -EMSGSIZE;
978
Oren Weilfb7d8792011-05-15 13:43:42 +0300979 /* complete all waiting for write CB */
980 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
981
982 list = &dev->write_waiting_list;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200983 list_for_each_entry_safe(pos, next, &list->list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +0200984 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200985 if (cl == NULL)
986 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +0300987
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200988 cl->status = 0;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200989 list_del(&pos->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200990 if (MEI_WRITING == cl->writing_state &&
991 (pos->major_file_operations == MEI_WRITE) &&
992 (cl != &dev->iamthif_cl)) {
Tomas Winkler483136e2012-07-04 19:24:54 +0300993 dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200994 cl->writing_state = MEI_WRITE_COMPLETE;
Tomas Winklerfb601ad2012-10-15 12:06:48 +0200995 list_add_tail(&pos->list, &cmpl_list->list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200996 }
997 if (cl == &dev->iamthif_cl) {
998 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
999 if (dev->iamthif_flow_control_pending) {
Tomas Winkler19838fb2012-11-01 21:17:15 +02001000 ret = mei_amthif_irq_read(dev, slots);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001001 if (ret)
1002 return ret;
1003 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001004 }
1005 }
1006
Tomas Winklerc216fde2012-08-16 19:39:43 +03001007 if (dev->wd_state == MEI_WD_STOPPING) {
1008 dev->wd_state = MEI_WD_IDLE;
Oren Weilfb7d8792011-05-15 13:43:42 +03001009 wake_up_interruptible(&dev->wait_stop_wd);
Oren Weilfb7d8792011-05-15 13:43:42 +03001010 }
1011
1012 if (dev->extra_write_index) {
1013 dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n",
1014 dev->extra_write_index);
1015 mei_write_message(dev,
1016 (struct mei_msg_hdr *) &dev->ext_msg_buf[0],
1017 (unsigned char *) &dev->ext_msg_buf[1],
1018 (dev->extra_write_index - 1) * sizeof(u32));
1019 *slots -= dev->extra_write_index;
1020 dev->extra_write_index = 0;
1021 }
Tomas Winklerb210d752012-08-07 00:03:56 +03001022 if (dev->dev_state == MEI_DEV_ENABLED) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001023 if (dev->wd_pending &&
Tomas Winkler483136e2012-07-04 19:24:54 +03001024 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001025 if (mei_wd_send(dev))
1026 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
Tomas Winkler483136e2012-07-04 19:24:54 +03001027 else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
1028 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +03001029
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001030 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +03001031
Tomas Winklerc216fde2012-08-16 19:39:43 +03001032 if (dev->wd_state == MEI_WD_RUNNING)
Tomas Winkler248ffdf2012-08-16 19:39:42 +03001033 *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
Tomas Winklerd242a0a2012-07-04 19:24:50 +03001034 else
Tomas Winkler248ffdf2012-08-16 19:39:42 +03001035 *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
Oren Weilfb7d8792011-05-15 13:43:42 +03001036 }
1037 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001038
1039 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +02001040 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001041 list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001042 cl = pos->cl;
Tomas Winklerc8372092011-11-27 21:43:33 +02001043 if (!cl) {
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001044 list_del(&pos->list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001045 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +03001046 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001047 switch (pos->major_file_operations) {
Tomas Winklerc8372092011-11-27 21:43:33 +02001048 case MEI_CLOSE:
1049 /* send disconnect message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001050 ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001051 if (ret)
1052 return ret;
1053
1054 break;
1055 case MEI_READ:
1056 /* send flow control message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001057 ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001058 if (ret)
1059 return ret;
1060
1061 break;
1062 case MEI_IOCTL:
1063 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +02001064 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +02001065 continue;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001066 ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001067 if (ret)
1068 return ret;
1069
1070 break;
1071
1072 default:
1073 BUG();
1074 }
1075
Oren Weilfb7d8792011-05-15 13:43:42 +03001076 }
1077 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001078 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001079 list_for_each_entry_safe(pos, next, &dev->write_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001080 cl = pos->cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001081 if (cl == NULL)
1082 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001083
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001084 if (cl != &dev->iamthif_cl) {
Tomas Winklerd2041152012-06-19 09:13:34 +03001085 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001086 dev_dbg(&dev->pdev->dev,
Tomas Winkler483136e2012-07-04 19:24:54 +03001087 "No flow control credentials for client %d, not sending.\n",
1088 cl->host_client_id);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001089 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001090 }
Tomas Winkler483136e2012-07-04 19:24:54 +03001091 ret = _mei_irq_thread_cmpl(dev, slots, pos,
1092 cl, cmpl_list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001093 if (ret)
1094 return ret;
1095
1096 } else if (cl == &dev->iamthif_cl) {
1097 /* IAMTHIF IOCTL */
1098 dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n");
Tomas Winklerd2041152012-06-19 09:13:34 +03001099 if (mei_flow_ctrl_creds(dev, cl) <= 0) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001100 dev_dbg(&dev->pdev->dev,
Tomas Winkler483136e2012-07-04 19:24:54 +03001101 "No flow control credentials for amthi client %d.\n",
1102 cl->host_client_id);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001103 continue;
1104 }
Tomas Winkler19838fb2012-11-01 21:17:15 +02001105 ret = mei_amthif_irq_process_completed(dev, slots, pos,
1106 cl, cmpl_list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001107 if (ret)
1108 return ret;
Oren Weilfb7d8792011-05-15 13:43:42 +03001109
1110 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001111
Oren Weilfb7d8792011-05-15 13:43:42 +03001112 }
1113 return 0;
1114}
1115
1116
1117
1118/**
1119 * mei_timer - timer function.
1120 *
1121 * @work: pointer to the work_struct structure
1122 *
1123 * NOTE: This function is called by timer interrupt work
1124 */
Oren Weila61c6532011-09-07 09:03:13 +03001125void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +03001126{
1127 unsigned long timeout;
1128 struct mei_cl *cl_pos = NULL;
1129 struct mei_cl *cl_next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001130 struct mei_cl_cb *cb_pos = NULL;
1131 struct mei_cl_cb *cb_next = NULL;
1132
1133 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +03001134 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +03001135
1136
1137 mutex_lock(&dev->device_lock);
Tomas Winklerb210d752012-08-07 00:03:56 +03001138 if (dev->dev_state != MEI_DEV_ENABLED) {
1139 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001140 if (dev->init_clients_timer) {
1141 if (--dev->init_clients_timer == 0) {
1142 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
1143 dev->init_clients_state);
1144 mei_reset(dev, 1);
1145 }
1146 }
1147 }
1148 goto out;
1149 }
1150 /*** connect/disconnect timeouts ***/
1151 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
1152 if (cl_pos->timer_count) {
1153 if (--cl_pos->timer_count == 0) {
1154 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
1155 mei_reset(dev, 1);
1156 goto out;
1157 }
1158 }
1159 }
1160
Oren Weilfb7d8792011-05-15 13:43:42 +03001161 if (dev->iamthif_stall_timer) {
1162 if (--dev->iamthif_stall_timer == 0) {
Masanari Iida32de21f2012-01-25 23:14:56 +09001163 dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n");
Oren Weilfb7d8792011-05-15 13:43:42 +03001164 mei_reset(dev, 1);
1165 dev->iamthif_msg_buf_size = 0;
1166 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001167 dev->iamthif_canceled = false;
1168 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001169 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1170 dev->iamthif_timer = 0;
1171
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001172 mei_io_cb_free(dev->iamthif_current_cb);
1173 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001174
1175 dev->iamthif_file_object = NULL;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001176 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001177 }
1178 }
1179
1180 if (dev->iamthif_timer) {
1181
1182 timeout = dev->iamthif_timer +
Tomas Winkler3870c322012-11-01 21:17:14 +02001183 mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER);
Oren Weilfb7d8792011-05-15 13:43:42 +03001184
1185 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
1186 dev->iamthif_timer);
1187 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
1188 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
1189 if (time_after(jiffies, timeout)) {
1190 /*
1191 * User didn't read the AMTHI data on time (15sec)
1192 * freeing AMTHI for other requests
1193 */
1194
1195 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
1196
Tomas Winklere773efc2012-11-11 17:37:58 +02001197 list_for_each_entry_safe(cb_pos, cb_next,
1198 &dev->amthif_rd_complete_list.list, list) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001199
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001200 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +03001201
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001202 /* Finding the AMTHI entry. */
1203 if (cl_pos == &dev->iamthif_cl)
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001204 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001205 }
Tomas Winkler601a1ef2012-10-09 16:50:20 +02001206 mei_io_cb_free(dev->iamthif_current_cb);
1207 dev->iamthif_current_cb = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001208
1209 dev->iamthif_file_object->private_data = NULL;
1210 dev->iamthif_file_object = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001211 dev->iamthif_timer = 0;
Tomas Winkler19838fb2012-11-01 21:17:15 +02001212 mei_amthif_run_next_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001213
1214 }
1215 }
1216out:
Tomas Winkler441ab502011-12-13 23:39:34 +02001217 schedule_delayed_work(&dev->timer_work, 2 * HZ);
1218 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +03001219}
1220
1221/**
1222 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
1223 * processing.
1224 *
1225 * @irq: The irq number
1226 * @dev_id: pointer to the device structure
1227 *
1228 * returns irqreturn_t
1229 *
1230 */
1231irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
1232{
1233 struct mei_device *dev = (struct mei_device *) dev_id;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001234 struct mei_cl_cb complete_list;
Oren Weilfb7d8792011-05-15 13:43:42 +03001235 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
1236 struct mei_cl *cl;
1237 s32 slots;
1238 int rets;
1239 bool bus_message_received;
1240
1241
1242 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
1243 /* initialize our complete list */
1244 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +03001245 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001246 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001247
1248 /* Ack the interrupt here
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001249 * In case of MSI we don't go through the quick handler */
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001250 if (pci_dev_msi_enabled(dev->pdev))
1251 mei_reg_write(dev, H_CSR, dev->host_hw_state);
1252
Oren Weilfb7d8792011-05-15 13:43:42 +03001253 dev->me_hw_state = mei_mecsr_read(dev);
1254
1255 /* check if ME wants a reset */
1256 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
Tomas Winklerb210d752012-08-07 00:03:56 +03001257 dev->dev_state != MEI_DEV_RESETING &&
1258 dev->dev_state != MEI_DEV_INITIALIZING) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001259 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1260 mei_reset(dev, 1);
1261 mutex_unlock(&dev->device_lock);
1262 return IRQ_HANDLED;
1263 }
1264
1265 /* check if we need to start the dev */
1266 if ((dev->host_hw_state & H_RDY) == 0) {
1267 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
1268 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
1269 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
1270 mei_hcsr_set(dev);
Tomas Winklerb210d752012-08-07 00:03:56 +03001271 dev->dev_state = MEI_DEV_INIT_CLIENTS;
Oren Weilfb7d8792011-05-15 13:43:42 +03001272 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
1273 /* link is established
1274 * start sending messages.
1275 */
Tomas Winklerc95efb72011-05-25 17:28:21 +03001276 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001277 mutex_unlock(&dev->device_lock);
1278 return IRQ_HANDLED;
1279 } else {
1280 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1281 mutex_unlock(&dev->device_lock);
1282 return IRQ_HANDLED;
1283 }
1284 }
Justin P. Mattock5f9092f32012-03-12 07:18:09 -07001285 /* check slots available for reading */
Oren Weilfb7d8792011-05-15 13:43:42 +03001286 slots = mei_count_full_read_slots(dev);
1287 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1288 slots, dev->extra_write_index);
1289 while (slots > 0 && !dev->extra_write_index) {
1290 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1291 slots, dev->extra_write_index);
1292 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
1293 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
1294 if (rets)
1295 goto end;
1296 }
1297 rets = mei_irq_thread_write_handler(&complete_list, dev, &slots);
1298end:
1299 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
1300 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler726917f2012-06-25 23:46:28 +03001301 dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001302
1303 bus_message_received = false;
1304 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
1305 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
1306 bus_message_received = true;
1307 }
1308 mutex_unlock(&dev->device_lock);
1309 if (bus_message_received) {
1310 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
1311 wake_up_interruptible(&dev->wait_recvd_msg);
1312 bus_message_received = false;
1313 }
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001314 if (list_empty(&complete_list.list))
Oren Weilfb7d8792011-05-15 13:43:42 +03001315 return IRQ_HANDLED;
1316
1317
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001318 list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) {
Tomas Winklerdb3ed432012-11-11 17:37:59 +02001319 cl = cb_pos->cl;
Tomas Winklerfb601ad2012-10-15 12:06:48 +02001320 list_del(&cb_pos->list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001321 if (cl) {
1322 if (cl != &dev->iamthif_cl) {
1323 dev_dbg(&dev->pdev->dev, "completing call back.\n");
1324 _mei_cmpl(cl, cb_pos);
1325 cb_pos = NULL;
1326 } else if (cl == &dev->iamthif_cl) {
Tomas Winkler19838fb2012-11-01 21:17:15 +02001327 mei_amthif_complete(dev, cb_pos);
Oren Weilfb7d8792011-05-15 13:43:42 +03001328 }
1329 }
1330 }
1331 return IRQ_HANDLED;
1332}