blob: c7822c9d2676a6c49ade5dd0f914041af9e207b8 [file] [log] [blame]
Oren Weilfb7d8792011-05-15 13:43:42 +03001/*
2 *
3 * Intel Management Engine Interface (Intel MEI) Linux driver
4 * Copyright (c) 2003-2011, Intel Corporation.
5 *
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"
25#include "mei.h"
26#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) {
61 mei_free_cb_private(cb_pos);
62 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/**
77 * _mei_cmpl_iamthif - processes completed iamthif operation.
78 *
79 * @dev: the device structure.
80 * @cb_pos: callback block.
81 */
82static void _mei_cmpl_iamthif(struct mei_device *dev, struct mei_cl_cb *cb_pos)
83{
84 if (dev->iamthif_canceled != 1) {
85 dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE;
86 dev->iamthif_stall_timer = 0;
87 memcpy(cb_pos->response_buffer.data,
88 dev->iamthif_msg_buf,
89 dev->iamthif_msg_buf_index);
90 list_add_tail(&cb_pos->cb_list,
91 &dev->amthi_read_complete_list.mei_cb.cb_list);
92 dev_dbg(&dev->pdev->dev, "amthi read completed.\n");
93 dev->iamthif_timer = jiffies;
94 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
95 dev->iamthif_timer);
96 } else {
Tomas Winklerc95efb72011-05-25 17:28:21 +030097 mei_run_next_iamthif_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +030098 }
99
100 dev_dbg(&dev->pdev->dev, "completing amthi call back.\n");
101 wake_up_interruptible(&dev->iamthif_cl.wait);
102}
103
104
105/**
106 * mei_irq_thread_read_amthi_message - bottom half read routine after ISR to
107 * handle the read amthi message data processing.
108 *
109 * @complete_list: An instance of our list structure
110 * @dev: the device structure
111 * @mei_hdr: header of amthi message
112 *
113 * returns 0 on success, <0 on failure.
114 */
115static int mei_irq_thread_read_amthi_message(struct mei_io_list *complete_list,
116 struct mei_device *dev,
117 struct mei_msg_hdr *mei_hdr)
118{
119 struct mei_cl *cl;
120 struct mei_cl_cb *cb;
121 unsigned char *buffer;
122
123 BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id);
124 BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING);
125
126 buffer = (unsigned char *) (dev->iamthif_msg_buf +
127 dev->iamthif_msg_buf_index);
128 BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length);
129
130 mei_read_slots(dev, buffer, mei_hdr->length);
131
132 dev->iamthif_msg_buf_index += mei_hdr->length;
133
134 if (!mei_hdr->msg_complete)
135 return 0;
136
137 dev_dbg(&dev->pdev->dev,
138 "amthi_message_buffer_index =%d\n",
139 mei_hdr->length);
140
141 dev_dbg(&dev->pdev->dev, "completed amthi read.\n ");
142 if (!dev->iamthif_current_cb)
143 return -ENODEV;
144
145 cb = dev->iamthif_current_cb;
146 dev->iamthif_current_cb = NULL;
147
148 cl = (struct mei_cl *)cb->file_private;
149 if (!cl)
150 return -ENODEV;
151
152 dev->iamthif_stall_timer = 0;
153 cb->information = dev->iamthif_msg_buf_index;
154 cb->read_time = jiffies;
155 if (dev->iamthif_ioctl && cl == &dev->iamthif_cl) {
156 /* found the iamthif cb */
157 dev_dbg(&dev->pdev->dev, "complete the amthi read cb.\n ");
158 dev_dbg(&dev->pdev->dev, "add the amthi read cb to complete.\n ");
159 list_add_tail(&cb->cb_list,
160 &complete_list->mei_cb.cb_list);
161 }
162 return 0;
163}
164
165/**
166 * _mei_irq_thread_state_ok - checks if mei header matches file private data
167 *
168 * @cl: private data of the file object
169 * @mei_hdr: header of mei client message
170 *
171 * returns !=0 if matches, 0 if no match.
172 */
173static int _mei_irq_thread_state_ok(struct mei_cl *cl,
174 struct mei_msg_hdr *mei_hdr)
175{
176 return (cl->host_client_id == mei_hdr->host_addr &&
177 cl->me_client_id == mei_hdr->me_addr &&
178 cl->state == MEI_FILE_CONNECTED &&
179 MEI_READ_COMPLETE != cl->reading_state);
180}
181
182/**
183 * mei_irq_thread_read_client_message - bottom half read routine after ISR to
184 * handle the read mei client message data processing.
185 *
186 * @complete_list: An instance of our list structure
187 * @dev: the device structure
188 * @mei_hdr: header of mei client message
189 *
190 * returns 0 on success, <0 on failure.
191 */
192static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
193 struct mei_device *dev,
194 struct mei_msg_hdr *mei_hdr)
195{
196 struct mei_cl *cl;
197 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
Tomas Winkler479bc592011-06-16 00:46:03 +0300198 unsigned char *buffer = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +0300199
200 dev_dbg(&dev->pdev->dev, "start client msg\n");
Tomas Winklerc8372092011-11-27 21:43:33 +0200201 if (list_empty(&dev->read_list.mei_cb.cb_list))
Oren Weilfb7d8792011-05-15 13:43:42 +0300202 goto quit;
203
204 list_for_each_entry_safe(cb_pos, cb_next,
205 &dev->read_list.mei_cb.cb_list, cb_list) {
206 cl = (struct mei_cl *)cb_pos->file_private;
207 if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) {
208 cl->reading_state = MEI_READING;
209 buffer = (unsigned char *)
210 (cb_pos->response_buffer.data +
211 cb_pos->information);
Oren Weilfb7d8792011-05-15 13:43:42 +0300212
213 if (cb_pos->response_buffer.size <
214 mei_hdr->length + cb_pos->information) {
215 dev_dbg(&dev->pdev->dev, "message overflow.\n");
216 list_del(&cb_pos->cb_list);
217 return -ENOMEM;
218 }
219 if (buffer)
220 mei_read_slots(dev, buffer, mei_hdr->length);
221
222 cb_pos->information += mei_hdr->length;
223 if (mei_hdr->msg_complete) {
224 cl->status = 0;
225 list_del(&cb_pos->cb_list);
226 dev_dbg(&dev->pdev->dev,
227 "completed read host client = %d,"
228 "ME client = %d, "
229 "data length = %lu\n",
230 cl->host_client_id,
231 cl->me_client_id,
232 cb_pos->information);
233
234 *(cb_pos->response_buffer.data +
235 cb_pos->information) = '\0';
236 dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n",
237 cb_pos->response_buffer.data);
238 list_add_tail(&cb_pos->cb_list,
239 &complete_list->mei_cb.cb_list);
240 }
241
242 break;
243 }
244
245 }
246
247quit:
248 dev_dbg(&dev->pdev->dev, "message read\n");
249 if (!buffer) {
250 mei_read_slots(dev, (unsigned char *) dev->rd_msg_buf,
251 mei_hdr->length);
252 dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n",
253 *(u32 *) dev->rd_msg_buf);
254 }
255
256 return 0;
257}
258
259/**
260 * _mei_irq_thread_iamthif_read - prepares to read iamthif data.
261 *
262 * @dev: the device structure.
263 * @slots: free slots.
264 *
265 * returns 0, OK; otherwise, error.
266 */
267static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots)
268{
269
270 if (((*slots) * sizeof(u32)) >= (sizeof(struct mei_msg_hdr)
271 + sizeof(struct hbm_flow_control))) {
272 *slots -= (sizeof(struct mei_msg_hdr) +
273 sizeof(struct hbm_flow_control) + 3) / 4;
274 if (!mei_send_flow_control(dev, &dev->iamthif_cl)) {
275 dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n");
276 } else {
277 dev_dbg(&dev->pdev->dev, "iamthif flow control success\n");
278 dev->iamthif_state = MEI_IAMTHIF_READING;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300279 dev->iamthif_flow_control_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300280 dev->iamthif_msg_buf_index = 0;
281 dev->iamthif_msg_buf_size = 0;
282 dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER;
283 dev->mei_host_buffer_is_empty =
284 mei_host_buffer_is_empty(dev);
285 }
286 return 0;
287 } else {
288 return -EMSGSIZE;
289 }
290}
291
292/**
293 * _mei_irq_thread_close - processes close related operation.
294 *
295 * @dev: the device structure.
296 * @slots: free slots.
297 * @cb_pos: callback block.
298 * @cl: private data of the file object.
299 * @cmpl_list: complete list.
300 *
301 * returns 0, OK; otherwise, error.
302 */
303static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots,
304 struct mei_cl_cb *cb_pos,
305 struct mei_cl *cl,
306 struct mei_io_list *cmpl_list)
307{
308 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
309 sizeof(struct hbm_client_disconnect_request))) {
310 *slots -= (sizeof(struct mei_msg_hdr) +
311 sizeof(struct hbm_client_disconnect_request) + 3) / 4;
312
313 if (!mei_disconnect(dev, cl)) {
314 cl->status = 0;
315 cb_pos->information = 0;
316 list_move_tail(&cb_pos->cb_list,
317 &cmpl_list->mei_cb.cb_list);
318 return -EMSGSIZE;
319 } else {
320 cl->state = MEI_FILE_DISCONNECTING;
321 cl->status = 0;
322 cb_pos->information = 0;
323 list_move_tail(&cb_pos->cb_list,
324 &dev->ctrl_rd_list.mei_cb.cb_list);
325 cl->timer_count = MEI_CONNECT_TIMEOUT;
326 }
327 } else {
328 /* return the cancel routine */
329 return -EBADMSG;
330 }
331
332 return 0;
333}
334
335/**
336 * is_treat_specially_client - checks if the message belongs
337 * to the file private data.
338 *
339 * @cl: private data of the file object
340 * @rs: connect response bus message
341 *
342 */
343static bool is_treat_specially_client(struct mei_cl *cl,
344 struct hbm_client_connect_response *rs)
345{
346
347 if (cl->host_client_id == rs->host_addr &&
348 cl->me_client_id == rs->me_addr) {
349 if (!rs->status) {
350 cl->state = MEI_FILE_CONNECTED;
351 cl->status = 0;
352
353 } else {
354 cl->state = MEI_FILE_DISCONNECTED;
355 cl->status = -ENODEV;
356 }
357 cl->timer_count = 0;
358
359 return true;
360 }
361 return false;
362}
363
364/**
365 * mei_client_connect_response - connects to response irq routine
366 *
367 * @dev: the device structure
368 * @rs: connect response bus message
369 */
370static void mei_client_connect_response(struct mei_device *dev,
371 struct hbm_client_connect_response *rs)
372{
373
374 struct mei_cl *cl;
375 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
376
377 dev_dbg(&dev->pdev->dev,
378 "connect_response:\n"
379 "ME Client = %d\n"
380 "Host Client = %d\n"
381 "Status = %d\n",
382 rs->me_addr,
383 rs->host_addr,
384 rs->status);
385
386 /* if WD or iamthif client treat specially */
387
388 if (is_treat_specially_client(&(dev->wd_cl), rs)) {
389 dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n",
390 dev->wd_timeout);
391
392 dev->wd_due_counter = (dev->wd_timeout) ? 1 : 0;
393
394 dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n");
Oren Weil9ce178e2011-09-07 09:03:09 +0300395
396 /* Registering watchdog interface device once we got connection
397 to the WD Client
398 */
399 if (watchdog_register_device(&amt_wd_dev)) {
400 printk(KERN_ERR "mei: unable to register watchdog device.\n");
401 dev->wd_interface_reg = false;
402 } else {
403 dev_dbg(&dev->pdev->dev, "successfully register watchdog interface.\n");
404 dev->wd_interface_reg = true;
405 }
406
Tomas Winklerc95efb72011-05-25 17:28:21 +0300407 mei_host_init_iamthif(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300408 return;
409 }
410
411 if (is_treat_specially_client(&(dev->iamthif_cl), rs)) {
412 dev->iamthif_state = MEI_IAMTHIF_IDLE;
413 return;
414 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200415 list_for_each_entry_safe(cb_pos, cb_next,
416 &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
417
418 cl = (struct mei_cl *)cb_pos->file_private;
419 if (!cl) {
420 list_del(&cb_pos->cb_list);
421 return;
422 }
423 if (MEI_IOCTL == cb_pos->major_file_operations) {
424 if (is_treat_specially_client(cl, rs)) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300425 list_del(&cb_pos->cb_list);
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200426 cl->status = 0;
427 cl->timer_count = 0;
428 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300429 }
430 }
431 }
432}
433
434/**
435 * mei_client_disconnect_response - disconnects from response irq routine
436 *
437 * @dev: the device structure
438 * @rs: disconnect response bus message
439 */
440static void mei_client_disconnect_response(struct mei_device *dev,
441 struct hbm_client_connect_response *rs)
442{
443 struct mei_cl *cl;
444 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
445
446 dev_dbg(&dev->pdev->dev,
447 "disconnect_response:\n"
448 "ME Client = %d\n"
449 "Host Client = %d\n"
450 "Status = %d\n",
451 rs->me_addr,
452 rs->host_addr,
453 rs->status);
454
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200455 list_for_each_entry_safe(cb_pos, cb_next,
456 &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) {
457 cl = (struct mei_cl *)cb_pos->file_private;
Oren Weilfb7d8792011-05-15 13:43:42 +0300458
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200459 if (!cl) {
460 list_del(&cb_pos->cb_list);
461 return;
462 }
Oren Weilfb7d8792011-05-15 13:43:42 +0300463
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200464 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n");
465 if (cl->host_client_id == rs->host_addr &&
466 cl->me_client_id == rs->me_addr) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300467
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200468 list_del(&cb_pos->cb_list);
469 if (!rs->status)
470 cl->state = MEI_FILE_DISCONNECTED;
Oren Weilfb7d8792011-05-15 13:43:42 +0300471
Tomas Winklerb7cd2d92011-11-27 21:43:34 +0200472 cl->status = 0;
473 cl->timer_count = 0;
474 break;
Oren Weilfb7d8792011-05-15 13:43:42 +0300475 }
476 }
477}
478
479/**
480 * same_flow_addr - tells if they have the same address.
481 *
482 * @file: private data of the file object.
483 * @flow: flow control.
484 *
485 * returns !=0, same; 0,not.
486 */
487static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow)
488{
489 return (cl->host_client_id == flow->host_addr &&
490 cl->me_client_id == flow->me_addr);
491}
492
493/**
494 * add_single_flow_creds - adds single buffer credentials.
495 *
496 * @file: private data ot the file object.
497 * @flow: flow control.
498 */
499static void add_single_flow_creds(struct mei_device *dev,
500 struct hbm_flow_control *flow)
501{
502 struct mei_me_client *client;
503 int i;
504
Tomas Winklercf9673d2011-06-06 10:44:33 +0300505 for (i = 0; i < dev->me_clients_num; i++) {
Oren Weilfb7d8792011-05-15 13:43:42 +0300506 client = &dev->me_clients[i];
507 if (client && flow->me_addr == client->client_id) {
508 if (client->props.single_recv_buf) {
509 client->mei_flow_ctrl_creds++;
510 dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n",
511 flow->me_addr);
512 dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n",
513 client->mei_flow_ctrl_creds);
514 } else {
515 BUG(); /* error in flow control */
516 }
517 }
518 }
519}
520
521/**
522 * mei_client_flow_control_response - flow control response irq routine
523 *
524 * @dev: the device structure
525 * @flow_control: flow control response bus message
526 */
527static void mei_client_flow_control_response(struct mei_device *dev,
528 struct hbm_flow_control *flow_control)
529{
530 struct mei_cl *cl_pos = NULL;
531 struct mei_cl *cl_next = NULL;
532
533 if (!flow_control->host_addr) {
534 /* single receive buffer */
535 add_single_flow_creds(dev, flow_control);
536 } else {
537 /* normal connection */
538 list_for_each_entry_safe(cl_pos, cl_next,
539 &dev->file_list, link) {
540 dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n");
541
542 dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n",
543 cl_pos->host_client_id,
544 cl_pos->me_client_id);
545 dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n",
546 flow_control->host_addr,
547 flow_control->me_addr);
548 if (same_flow_addr(cl_pos, flow_control)) {
549 dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n",
550 flow_control->host_addr,
551 flow_control->me_addr);
552 cl_pos->mei_flow_ctrl_creds++;
553 dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n",
554 cl_pos->mei_flow_ctrl_creds);
555 break;
556 }
557 }
558 }
559}
560
561/**
562 * same_disconn_addr - tells if they have the same address
563 *
564 * @file: private data of the file object.
565 * @disconn: disconnection request.
566 *
567 * returns !=0, same; 0,not.
568 */
569static int same_disconn_addr(struct mei_cl *cl,
570 struct hbm_client_disconnect_request *disconn)
571{
572 return (cl->host_client_id == disconn->host_addr &&
573 cl->me_client_id == disconn->me_addr);
574}
575
576/**
577 * mei_client_disconnect_request - disconnects from request irq routine
578 *
579 * @dev: the device structure.
580 * @disconnect_req: disconnect request bus message.
581 */
582static void mei_client_disconnect_request(struct mei_device *dev,
583 struct hbm_client_disconnect_request *disconnect_req)
584{
585 struct mei_msg_hdr *mei_hdr;
586 struct hbm_client_connect_response *disconnect_res;
587 struct mei_cl *cl_pos = NULL;
588 struct mei_cl *cl_next = NULL;
589
590 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
591 if (same_disconn_addr(cl_pos, disconnect_req)) {
592 dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n",
593 disconnect_req->host_addr,
594 disconnect_req->me_addr);
595 cl_pos->state = MEI_FILE_DISCONNECTED;
596 cl_pos->timer_count = 0;
597 if (cl_pos == &dev->wd_cl) {
598 dev->wd_due_counter = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300599 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300600 } else if (cl_pos == &dev->iamthif_cl)
601 dev->iamthif_timer = 0;
602
603 /* prepare disconnect response */
604 mei_hdr =
605 (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
606 mei_hdr->host_addr = 0;
607 mei_hdr->me_addr = 0;
608 mei_hdr->length =
609 sizeof(struct hbm_client_connect_response);
610 mei_hdr->msg_complete = 1;
611 mei_hdr->reserved = 0;
612
613 disconnect_res =
614 (struct hbm_client_connect_response *)
615 &dev->ext_msg_buf[1];
616 disconnect_res->host_addr = cl_pos->host_client_id;
617 disconnect_res->me_addr = cl_pos->me_client_id;
618 *(u8 *) (&disconnect_res->cmd) =
619 CLIENT_DISCONNECT_RES_CMD;
620 disconnect_res->status = 0;
621 dev->extra_write_index = 2;
622 break;
623 }
624 }
625}
626
627
628/**
629 * mei_irq_thread_read_bus_message - bottom half read routine after ISR to
630 * handle the read bus message cmd processing.
631 *
632 * @dev: the device structure
633 * @mei_hdr: header of bus message
634 */
635static void mei_irq_thread_read_bus_message(struct mei_device *dev,
636 struct mei_msg_hdr *mei_hdr)
637{
638 struct mei_bus_message *mei_msg;
639 struct hbm_host_version_response *version_res;
640 struct hbm_client_connect_response *connect_res;
641 struct hbm_client_connect_response *disconnect_res;
642 struct hbm_flow_control *flow_control;
643 struct hbm_props_response *props_res;
644 struct hbm_host_enum_response *enum_res;
645 struct hbm_client_disconnect_request *disconnect_req;
646 struct hbm_host_stop_request *host_stop_req;
Oren Weilabc51b62011-09-21 16:45:30 +0300647 int res;
Oren Weilfb7d8792011-05-15 13:43:42 +0300648
649 unsigned char *buffer;
650
651 /* read the message to our buffer */
652 buffer = (unsigned char *) dev->rd_msg_buf;
653 BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf));
654 mei_read_slots(dev, buffer, mei_hdr->length);
655 mei_msg = (struct mei_bus_message *) buffer;
656
657 switch (*(u8 *) mei_msg) {
658 case HOST_START_RES_CMD:
659 version_res = (struct hbm_host_version_response *) mei_msg;
660 if (version_res->host_version_supported) {
661 dev->version.major_version = HBM_MAJOR_VERSION;
662 dev->version.minor_version = HBM_MINOR_VERSION;
663 if (dev->mei_state == MEI_INIT_CLIENTS &&
664 dev->init_clients_state == MEI_START_MESSAGE) {
665 dev->init_clients_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300666 mei_host_enum_clients_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300667 } else {
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300668 dev->recvd_msg = false;
Oren Weilfb7d8792011-05-15 13:43:42 +0300669 dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n");
670 mei_reset(dev, 1);
671 return;
672 }
673 } else {
674 dev->version = version_res->me_max_version;
675 /* send stop message */
676 mei_hdr->host_addr = 0;
677 mei_hdr->me_addr = 0;
678 mei_hdr->length = sizeof(struct hbm_host_stop_request);
679 mei_hdr->msg_complete = 1;
680 mei_hdr->reserved = 0;
681
682 host_stop_req = (struct hbm_host_stop_request *)
683 &dev->wr_msg_buf[1];
684
685 memset(host_stop_req,
686 0,
687 sizeof(struct hbm_host_stop_request));
688 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
689 host_stop_req->reason = DRIVER_STOP_REQUEST;
690 mei_write_message(dev, mei_hdr,
691 (unsigned char *) (host_stop_req),
692 mei_hdr->length);
693 dev_dbg(&dev->pdev->dev, "version mismatch.\n");
694 return;
695 }
696
Tomas Winklereb9af0a2011-05-25 17:28:22 +0300697 dev->recvd_msg = true;
Oren Weilfb7d8792011-05-15 13:43:42 +0300698 dev_dbg(&dev->pdev->dev, "host start response message received.\n");
699 break;
700
701 case CLIENT_CONNECT_RES_CMD:
702 connect_res =
703 (struct hbm_client_connect_response *) mei_msg;
704 mei_client_connect_response(dev, connect_res);
705 dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
706 wake_up(&dev->wait_recvd_msg);
707 break;
708
709 case CLIENT_DISCONNECT_RES_CMD:
710 disconnect_res =
711 (struct hbm_client_connect_response *) mei_msg;
Tomas Winkler441ab502011-12-13 23:39:34 +0200712 mei_client_disconnect_response(dev, disconnect_res);
Oren Weilfb7d8792011-05-15 13:43:42 +0300713 dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
714 wake_up(&dev->wait_recvd_msg);
715 break;
716
717 case MEI_FLOW_CONTROL_CMD:
718 flow_control = (struct hbm_flow_control *) mei_msg;
719 mei_client_flow_control_response(dev, flow_control);
720 dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
721 break;
722
723 case HOST_CLIENT_PROPERTIES_RES_CMD:
724 props_res = (struct hbm_props_response *)mei_msg;
725 if (props_res->status || !dev->me_clients) {
726 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n");
727 mei_reset(dev, 1);
728 return;
729 }
Tomas Winkler441ab502011-12-13 23:39:34 +0200730 if (dev->me_clients[dev->me_client_presentation_num]
Oren Weilfb7d8792011-05-15 13:43:42 +0300731 .client_id == props_res->address) {
732
733 dev->me_clients[dev->me_client_presentation_num].props
734 = props_res->client_properties;
735
736 if (dev->mei_state == MEI_INIT_CLIENTS &&
737 dev->init_clients_state ==
738 MEI_CLIENT_PROPERTIES_MESSAGE) {
739 dev->me_client_index++;
740 dev->me_client_presentation_num++;
Oren Weilabc51b62011-09-21 16:45:30 +0300741
742 /** Send Client Propeties request **/
743 res = mei_host_client_properties(dev);
744 if (res < 0) {
745 dev_dbg(&dev->pdev->dev, "mei_host_client_properties() failed");
746 return;
747 } else if (!res) {
748 /*
749 * No more clients to send to.
750 * Clear Map for indicating now ME clients
751 * with associated host client
752 */
753 bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
754 dev->open_handle_count = 0;
755
756 /*
757 * Reserving the first three client IDs
758 * Client Id 0 - Reserved for MEI Bus Message communications
759 * Client Id 1 - Reserved for Watchdog
760 * Client ID 2 - Reserved for AMTHI
761 */
762 bitmap_set(dev->host_clients_map, 0, 3);
763 dev->mei_state = MEI_ENABLED;
764
765 /* if wd initialization fails, initialization the AMTHI client,
766 * otherwise the AMTHI client will be initialized after the WD client connect response
767 * will be received
768 */
769 if (mei_wd_host_init(dev))
770 mei_host_init_iamthif(dev);
771 }
772
Oren Weilfb7d8792011-05-15 13:43:42 +0300773 } else {
774 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message");
775 mei_reset(dev, 1);
776 return;
777 }
778 } else {
779 dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n");
780 mei_reset(dev, 1);
781 return;
782 }
783 break;
784
785 case HOST_ENUM_RES_CMD:
786 enum_res = (struct hbm_host_enum_response *) mei_msg;
787 memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
788 if (dev->mei_state == MEI_INIT_CLIENTS &&
789 dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
790 dev->init_clients_timer = 0;
791 dev->me_client_presentation_num = 0;
792 dev->me_client_index = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300793 mei_allocate_me_clients_storage(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300794 dev->init_clients_state =
795 MEI_CLIENT_PROPERTIES_MESSAGE;
Tomas Winklerc95efb72011-05-25 17:28:21 +0300796 mei_host_client_properties(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +0300797 } else {
798 dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n");
799 mei_reset(dev, 1);
800 return;
801 }
802 break;
803
804 case HOST_STOP_RES_CMD:
805 dev->mei_state = MEI_DISABLED;
806 dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
807 mei_reset(dev, 1);
808 break;
809
810 case CLIENT_DISCONNECT_REQ_CMD:
811 /* search for client */
812 disconnect_req =
813 (struct hbm_client_disconnect_request *) mei_msg;
814 mei_client_disconnect_request(dev, disconnect_req);
815 break;
816
817 case ME_STOP_REQ_CMD:
818 /* prepare stop request */
819 mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0];
820 mei_hdr->host_addr = 0;
821 mei_hdr->me_addr = 0;
822 mei_hdr->length = sizeof(struct hbm_host_stop_request);
823 mei_hdr->msg_complete = 1;
824 mei_hdr->reserved = 0;
825 host_stop_req =
826 (struct hbm_host_stop_request *) &dev->ext_msg_buf[1];
827 memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request));
828 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
829 host_stop_req->reason = DRIVER_STOP_REQUEST;
830 host_stop_req->reserved[0] = 0;
831 host_stop_req->reserved[1] = 0;
832 dev->extra_write_index = 2;
833 break;
834
835 default:
836 BUG();
837 break;
838
839 }
840}
841
842
843/**
844 * _mei_hb_read - processes read related operation.
845 *
846 * @dev: the device structure.
847 * @slots: free slots.
848 * @cb_pos: callback block.
849 * @cl: private data of the file object.
850 * @cmpl_list: complete list.
851 *
852 * returns 0, OK; otherwise, error.
853 */
854static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots,
855 struct mei_cl_cb *cb_pos,
856 struct mei_cl *cl,
857 struct mei_io_list *cmpl_list)
858{
859 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
860 sizeof(struct hbm_flow_control))) {
861 *slots -= (sizeof(struct mei_msg_hdr) +
862 sizeof(struct hbm_flow_control) + 3) / 4;
863 if (!mei_send_flow_control(dev, cl)) {
864 cl->status = -ENODEV;
865 cb_pos->information = 0;
866 list_move_tail(&cb_pos->cb_list,
867 &cmpl_list->mei_cb.cb_list);
868 return -ENODEV;
869 } else {
870 list_move_tail(&cb_pos->cb_list,
871 &dev->read_list.mei_cb.cb_list);
872 }
873 } else {
874 /* return the cancel routine */
875 list_del(&cb_pos->cb_list);
876 return -EBADMSG;
877 }
878
879 return 0;
880}
881
882
883/**
884 * _mei_irq_thread_ioctl - processes ioctl related operation.
885 *
886 * @dev: the device structure.
887 * @slots: free slots.
888 * @cb_pos: callback block.
889 * @cl: private data of the file object.
890 * @cmpl_list: complete list.
891 *
892 * returns 0, OK; otherwise, error.
893 */
894static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots,
895 struct mei_cl_cb *cb_pos,
896 struct mei_cl *cl,
897 struct mei_io_list *cmpl_list)
898{
899 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
900 sizeof(struct hbm_client_connect_request))) {
901 cl->state = MEI_FILE_CONNECTING;
902 *slots -= (sizeof(struct mei_msg_hdr) +
903 sizeof(struct hbm_client_connect_request) + 3) / 4;
904 if (!mei_connect(dev, cl)) {
905 cl->status = -ENODEV;
906 cb_pos->information = 0;
907 list_del(&cb_pos->cb_list);
908 return -ENODEV;
909 } else {
910 list_move_tail(&cb_pos->cb_list,
911 &dev->ctrl_rd_list.mei_cb.cb_list);
912 cl->timer_count = MEI_CONNECT_TIMEOUT;
913 }
914 } else {
915 /* return the cancel routine */
916 list_del(&cb_pos->cb_list);
917 return -EBADMSG;
918 }
919
920 return 0;
921}
922
923/**
924 * _mei_irq_thread_cmpl - processes completed and no-iamthif operation.
925 *
926 * @dev: the device structure.
927 * @slots: free slots.
928 * @cb_pos: callback block.
929 * @cl: private data of the file object.
930 * @cmpl_list: complete list.
931 *
932 * returns 0, OK; otherwise, error.
933 */
934static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots,
935 struct mei_cl_cb *cb_pos,
936 struct mei_cl *cl,
937 struct mei_io_list *cmpl_list)
938{
939 struct mei_msg_hdr *mei_hdr;
940
941 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
942 (cb_pos->request_buffer.size -
943 cb_pos->information))) {
944 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
945 mei_hdr->host_addr = cl->host_client_id;
946 mei_hdr->me_addr = cl->me_client_id;
947 mei_hdr->length = cb_pos->request_buffer.size -
948 cb_pos->information;
949 mei_hdr->msg_complete = 1;
950 mei_hdr->reserved = 0;
951 dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d"
952 "mei_hdr->msg_complete = %d\n",
953 cb_pos->request_buffer.size,
954 mei_hdr->msg_complete);
955 dev_dbg(&dev->pdev->dev, "cb_pos->information =%lu\n",
956 cb_pos->information);
957 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
958 mei_hdr->length);
959 *slots -= (sizeof(struct mei_msg_hdr) +
960 mei_hdr->length + 3) / 4;
961 if (!mei_write_message(dev, mei_hdr,
962 (unsigned char *)
963 (cb_pos->request_buffer.data +
964 cb_pos->information),
965 mei_hdr->length)) {
966 cl->status = -ENODEV;
967 list_move_tail(&cb_pos->cb_list,
968 &cmpl_list->mei_cb.cb_list);
969 return -ENODEV;
970 } else {
971 if (mei_flow_ctrl_reduce(dev, cl))
972 return -ENODEV;
973 cl->status = 0;
974 cb_pos->information += mei_hdr->length;
975 list_move_tail(&cb_pos->cb_list,
976 &dev->write_waiting_list.mei_cb.cb_list);
977 }
978 } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
979 /* buffer is still empty */
980 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
981 mei_hdr->host_addr = cl->host_client_id;
982 mei_hdr->me_addr = cl->me_client_id;
983 mei_hdr->length =
984 (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
985 mei_hdr->msg_complete = 0;
986 mei_hdr->reserved = 0;
987
988 (*slots) -= (sizeof(struct mei_msg_hdr) +
989 mei_hdr->length + 3) / 4;
990 if (!mei_write_message(dev, mei_hdr,
991 (unsigned char *)
992 (cb_pos->request_buffer.data +
993 cb_pos->information),
994 mei_hdr->length)) {
995 cl->status = -ENODEV;
996 list_move_tail(&cb_pos->cb_list,
997 &cmpl_list->mei_cb.cb_list);
998 return -ENODEV;
999 } else {
1000 cb_pos->information += mei_hdr->length;
1001 dev_dbg(&dev->pdev->dev,
1002 "cb_pos->request_buffer.size =%d"
1003 " mei_hdr->msg_complete = %d\n",
1004 cb_pos->request_buffer.size,
1005 mei_hdr->msg_complete);
1006 dev_dbg(&dev->pdev->dev, "cb_pos->information =%lu\n",
1007 cb_pos->information);
1008 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
1009 mei_hdr->length);
1010 }
1011 return -EMSGSIZE;
1012 } else {
1013 return -EBADMSG;
1014 }
1015
1016 return 0;
1017}
1018
1019/**
1020 * _mei_irq_thread_cmpl_iamthif - processes completed iamthif operation.
1021 *
1022 * @dev: the device structure.
1023 * @slots: free slots.
1024 * @cb_pos: callback block.
1025 * @cl: private data of the file object.
1026 * @cmpl_list: complete list.
1027 *
1028 * returns 0, OK; otherwise, error.
1029 */
1030static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots,
1031 struct mei_cl_cb *cb_pos,
1032 struct mei_cl *cl,
1033 struct mei_io_list *cmpl_list)
1034{
1035 struct mei_msg_hdr *mei_hdr;
1036
1037 if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) +
1038 dev->iamthif_msg_buf_size -
1039 dev->iamthif_msg_buf_index)) {
1040 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
1041 mei_hdr->host_addr = cl->host_client_id;
1042 mei_hdr->me_addr = cl->me_client_id;
1043 mei_hdr->length = dev->iamthif_msg_buf_size -
1044 dev->iamthif_msg_buf_index;
1045 mei_hdr->msg_complete = 1;
1046 mei_hdr->reserved = 0;
1047
1048 *slots -= (sizeof(struct mei_msg_hdr) +
1049 mei_hdr->length + 3) / 4;
1050
1051 if (!mei_write_message(dev, mei_hdr,
1052 (dev->iamthif_msg_buf +
1053 dev->iamthif_msg_buf_index),
1054 mei_hdr->length)) {
1055 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1056 cl->status = -ENODEV;
1057 list_del(&cb_pos->cb_list);
1058 return -ENODEV;
1059 } else {
1060 if (mei_flow_ctrl_reduce(dev, cl))
1061 return -ENODEV;
1062 dev->iamthif_msg_buf_index += mei_hdr->length;
1063 cb_pos->information = dev->iamthif_msg_buf_index;
1064 cl->status = 0;
1065 dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001066 dev->iamthif_flow_control_pending = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001067 /* save iamthif cb sent to amthi client */
1068 dev->iamthif_current_cb = cb_pos;
1069 list_move_tail(&cb_pos->cb_list,
1070 &dev->write_waiting_list.mei_cb.cb_list);
1071
1072 }
1073 } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) {
1074 /* buffer is still empty */
1075 mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0];
1076 mei_hdr->host_addr = cl->host_client_id;
1077 mei_hdr->me_addr = cl->me_client_id;
1078 mei_hdr->length =
1079 (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr);
1080 mei_hdr->msg_complete = 0;
1081 mei_hdr->reserved = 0;
1082
1083 *slots -= (sizeof(struct mei_msg_hdr) +
1084 mei_hdr->length + 3) / 4;
1085
1086 if (!mei_write_message(dev, mei_hdr,
1087 (dev->iamthif_msg_buf +
1088 dev->iamthif_msg_buf_index),
1089 mei_hdr->length)) {
1090 cl->status = -ENODEV;
1091 list_del(&cb_pos->cb_list);
1092 } else {
1093 dev->iamthif_msg_buf_index += mei_hdr->length;
1094 }
1095 return -EMSGSIZE;
1096 } else {
1097 return -EBADMSG;
1098 }
1099
1100 return 0;
1101}
1102
1103/**
1104 * mei_irq_thread_read_handler - bottom half read routine after ISR to
1105 * handle the read processing.
1106 *
1107 * @cmpl_list: An instance of our list structure
1108 * @dev: the device structure
1109 * @slots: slots to read.
1110 *
1111 * returns 0 on success, <0 on failure.
1112 */
1113static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list,
1114 struct mei_device *dev,
1115 s32 *slots)
1116{
1117 struct mei_msg_hdr *mei_hdr;
1118 struct mei_cl *cl_pos = NULL;
1119 struct mei_cl *cl_next = NULL;
1120 int ret = 0;
1121
1122 if (!dev->rd_msg_hdr) {
1123 dev->rd_msg_hdr = mei_mecbrw_read(dev);
1124 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
1125 (*slots)--;
1126 dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
1127 }
1128 mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr;
1129 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length);
1130
1131 if (mei_hdr->reserved || !dev->rd_msg_hdr) {
1132 dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
1133 ret = -EBADMSG;
1134 goto end;
1135 }
1136
1137 if (mei_hdr->host_addr || mei_hdr->me_addr) {
1138 list_for_each_entry_safe(cl_pos, cl_next,
1139 &dev->file_list, link) {
1140 dev_dbg(&dev->pdev->dev,
1141 "list_for_each_entry_safe read host"
1142 " client = %d, ME client = %d\n",
1143 cl_pos->host_client_id,
1144 cl_pos->me_client_id);
1145 if (cl_pos->host_client_id == mei_hdr->host_addr &&
1146 cl_pos->me_client_id == mei_hdr->me_addr)
1147 break;
1148 }
1149
1150 if (&cl_pos->link == &dev->file_list) {
1151 dev_dbg(&dev->pdev->dev, "corrupted message header\n");
1152 ret = -EBADMSG;
1153 goto end;
1154 }
1155 }
1156 if (((*slots) * sizeof(u32)) < mei_hdr->length) {
1157 dev_dbg(&dev->pdev->dev,
1158 "we can't read the message slots =%08x.\n",
1159 *slots);
1160 /* we can't read the message */
1161 ret = -ERANGE;
1162 goto end;
1163 }
1164
1165 /* decide where to read the message too */
1166 if (!mei_hdr->host_addr) {
1167 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n");
1168 mei_irq_thread_read_bus_message(dev, mei_hdr);
1169 dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n");
1170 } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
1171 (MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
1172 (dev->iamthif_state == MEI_IAMTHIF_READING)) {
1173 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n");
1174 dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n",
1175 mei_hdr->length);
1176 ret = mei_irq_thread_read_amthi_message(cmpl_list,
1177 dev, mei_hdr);
1178 if (ret)
1179 goto end;
1180
1181 } else {
1182 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n");
1183 ret = mei_irq_thread_read_client_message(cmpl_list,
1184 dev, mei_hdr);
1185 if (ret)
1186 goto end;
1187
1188 }
1189
1190 /* reset the number of slots and header */
1191 *slots = mei_count_full_read_slots(dev);
1192 dev->rd_msg_hdr = 0;
1193
1194 if (*slots == -EOVERFLOW) {
1195 /* overflow - reset */
1196 dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n");
1197 /* set the event since message has been read */
1198 ret = -ERANGE;
1199 goto end;
1200 }
1201end:
1202 return ret;
1203}
1204
1205
1206/**
1207 * mei_irq_thread_write_handler - bottom half write routine after
1208 * ISR to handle the write processing.
1209 *
1210 * @cmpl_list: An instance of our list structure
1211 * @dev: the device structure
1212 * @slots: slots to write.
1213 *
1214 * returns 0 on success, <0 on failure.
1215 */
1216static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
1217 struct mei_device *dev,
1218 s32 *slots)
1219{
1220
1221 struct mei_cl *cl;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001222 struct mei_cl_cb *pos = NULL, *next = NULL;
Oren Weilfb7d8792011-05-15 13:43:42 +03001223 struct mei_io_list *list;
1224 int ret;
1225
1226 if (!mei_host_buffer_is_empty(dev)) {
1227 dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n");
1228 return 0;
1229 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001230 *slots = mei_count_empty_write_slots(dev);
1231 /* complete all waiting for write CB */
1232 dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n");
1233
1234 list = &dev->write_waiting_list;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001235 list_for_each_entry_safe(pos, next,
1236 &list->mei_cb.cb_list, cb_list) {
1237 cl = (struct mei_cl *)pos->file_private;
1238 if (cl == NULL)
1239 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001240
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001241 cl->status = 0;
1242 list_del(&pos->cb_list);
1243 if (MEI_WRITING == cl->writing_state &&
1244 (pos->major_file_operations == MEI_WRITE) &&
1245 (cl != &dev->iamthif_cl)) {
1246 dev_dbg(&dev->pdev->dev,
1247 "MEI WRITE COMPLETE\n");
1248 cl->writing_state = MEI_WRITE_COMPLETE;
1249 list_add_tail(&pos->cb_list,
1250 &cmpl_list->mei_cb.cb_list);
1251 }
1252 if (cl == &dev->iamthif_cl) {
1253 dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n");
1254 if (dev->iamthif_flow_control_pending) {
1255 ret = _mei_irq_thread_iamthif_read(
1256 dev, slots);
1257 if (ret)
1258 return ret;
1259 }
Oren Weilfb7d8792011-05-15 13:43:42 +03001260 }
1261 }
1262
1263 if (dev->stop && !dev->wd_pending) {
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001264 dev->wd_stopped = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001265 wake_up_interruptible(&dev->wait_stop_wd);
1266 return 0;
1267 }
1268
1269 if (dev->extra_write_index) {
1270 dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n",
1271 dev->extra_write_index);
1272 mei_write_message(dev,
1273 (struct mei_msg_hdr *) &dev->ext_msg_buf[0],
1274 (unsigned char *) &dev->ext_msg_buf[1],
1275 (dev->extra_write_index - 1) * sizeof(u32));
1276 *slots -= dev->extra_write_index;
1277 dev->extra_write_index = 0;
1278 }
1279 if (dev->mei_state == MEI_ENABLED) {
1280 if (dev->wd_pending &&
1281 mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
1282 if (mei_wd_send(dev))
1283 dev_dbg(&dev->pdev->dev, "wd send failed.\n");
1284 else
1285 if (mei_flow_ctrl_reduce(dev, &dev->wd_cl))
1286 return -ENODEV;
1287
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001288 dev->wd_pending = false;
Oren Weilfb7d8792011-05-15 13:43:42 +03001289
1290 if (dev->wd_timeout) {
1291 *slots -= (sizeof(struct mei_msg_hdr) +
1292 MEI_START_WD_DATA_SIZE + 3) / 4;
1293 dev->wd_due_counter = 2;
1294 } else {
1295 *slots -= (sizeof(struct mei_msg_hdr) +
1296 MEI_WD_PARAMS_SIZE + 3) / 4;
1297 dev->wd_due_counter = 0;
1298 }
1299
1300 }
1301 }
1302 if (dev->stop)
1303 return ~ENODEV;
1304
1305 /* complete control write list CB */
Tomas Winklerc8372092011-11-27 21:43:33 +02001306 dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001307 list_for_each_entry_safe(pos, next,
Oren Weilfb7d8792011-05-15 13:43:42 +03001308 &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001309 cl = (struct mei_cl *) pos->file_private;
Tomas Winklerc8372092011-11-27 21:43:33 +02001310 if (!cl) {
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001311 list_del(&pos->cb_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001312 return -ENODEV;
Oren Weilfb7d8792011-05-15 13:43:42 +03001313 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001314 switch (pos->major_file_operations) {
Tomas Winklerc8372092011-11-27 21:43:33 +02001315 case MEI_CLOSE:
1316 /* send disconnect message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001317 ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001318 if (ret)
1319 return ret;
1320
1321 break;
1322 case MEI_READ:
1323 /* send flow control message */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001324 ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001325 if (ret)
1326 return ret;
1327
1328 break;
1329 case MEI_IOCTL:
1330 /* connect message */
Natalia Ovsyanikove8cd29d2011-12-05 00:16:54 +02001331 if (mei_other_client_is_connecting(dev, cl))
Tomas Winklerc8372092011-11-27 21:43:33 +02001332 continue;
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001333 ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list);
Tomas Winklerc8372092011-11-27 21:43:33 +02001334 if (ret)
1335 return ret;
1336
1337 break;
1338
1339 default:
1340 BUG();
1341 }
1342
Oren Weilfb7d8792011-05-15 13:43:42 +03001343 }
1344 /* complete write list CB */
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001345 dev_dbg(&dev->pdev->dev, "complete write list cb.\n");
1346 list_for_each_entry_safe(pos, next,
1347 &dev->write_list.mei_cb.cb_list, cb_list) {
1348 cl = (struct mei_cl *)pos->file_private;
1349 if (cl == NULL)
1350 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001351
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001352 if (cl != &dev->iamthif_cl) {
1353 if (!mei_flow_ctrl_creds(dev, cl)) {
1354 dev_dbg(&dev->pdev->dev,
1355 "No flow control"
1356 " credentials for client"
1357 " %d, not sending.\n",
1358 cl->host_client_id);
1359 continue;
Oren Weilfb7d8792011-05-15 13:43:42 +03001360 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001361 ret = _mei_irq_thread_cmpl(dev, slots,
1362 pos,
1363 cl, cmpl_list);
1364 if (ret)
1365 return ret;
1366
1367 } else if (cl == &dev->iamthif_cl) {
1368 /* IAMTHIF IOCTL */
1369 dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n");
1370 if (!mei_flow_ctrl_creds(dev, cl)) {
1371 dev_dbg(&dev->pdev->dev,
1372 "No flow control"
1373 " credentials for amthi"
1374 " client %d.\n",
1375 cl->host_client_id);
1376 continue;
1377 }
1378 ret = _mei_irq_thread_cmpl_iamthif(dev,
1379 slots,
1380 pos,
1381 cl,
1382 cmpl_list);
1383 if (ret)
1384 return ret;
Oren Weilfb7d8792011-05-15 13:43:42 +03001385
1386 }
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001387
Oren Weilfb7d8792011-05-15 13:43:42 +03001388 }
1389 return 0;
1390}
1391
1392
1393
1394/**
1395 * mei_timer - timer function.
1396 *
1397 * @work: pointer to the work_struct structure
1398 *
1399 * NOTE: This function is called by timer interrupt work
1400 */
Oren Weila61c6532011-09-07 09:03:13 +03001401void mei_timer(struct work_struct *work)
Oren Weilfb7d8792011-05-15 13:43:42 +03001402{
1403 unsigned long timeout;
1404 struct mei_cl *cl_pos = NULL;
1405 struct mei_cl *cl_next = NULL;
1406 struct list_head *amthi_complete_list = NULL;
1407 struct mei_cl_cb *cb_pos = NULL;
1408 struct mei_cl_cb *cb_next = NULL;
1409
1410 struct mei_device *dev = container_of(work,
Oren Weila61c6532011-09-07 09:03:13 +03001411 struct mei_device, timer_work.work);
Oren Weilfb7d8792011-05-15 13:43:42 +03001412
1413
1414 mutex_lock(&dev->device_lock);
1415 if (dev->mei_state != MEI_ENABLED) {
1416 if (dev->mei_state == MEI_INIT_CLIENTS) {
1417 if (dev->init_clients_timer) {
1418 if (--dev->init_clients_timer == 0) {
1419 dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
1420 dev->init_clients_state);
1421 mei_reset(dev, 1);
1422 }
1423 }
1424 }
1425 goto out;
1426 }
1427 /*** connect/disconnect timeouts ***/
1428 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
1429 if (cl_pos->timer_count) {
1430 if (--cl_pos->timer_count == 0) {
1431 dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n");
1432 mei_reset(dev, 1);
1433 goto out;
1434 }
1435 }
1436 }
1437
Oren Weilfb7d8792011-05-15 13:43:42 +03001438 if (dev->iamthif_stall_timer) {
1439 if (--dev->iamthif_stall_timer == 0) {
1440 dev_dbg(&dev->pdev->dev, "reseting because of hang to amthi.\n");
1441 mei_reset(dev, 1);
1442 dev->iamthif_msg_buf_size = 0;
1443 dev->iamthif_msg_buf_index = 0;
Tomas Winklereb9af0a2011-05-25 17:28:22 +03001444 dev->iamthif_canceled = false;
1445 dev->iamthif_ioctl = true;
Oren Weilfb7d8792011-05-15 13:43:42 +03001446 dev->iamthif_state = MEI_IAMTHIF_IDLE;
1447 dev->iamthif_timer = 0;
1448
1449 if (dev->iamthif_current_cb)
1450 mei_free_cb_private(dev->iamthif_current_cb);
1451
1452 dev->iamthif_file_object = NULL;
1453 dev->iamthif_current_cb = NULL;
Tomas Winklerc95efb72011-05-25 17:28:21 +03001454 mei_run_next_iamthif_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001455 }
1456 }
1457
1458 if (dev->iamthif_timer) {
1459
1460 timeout = dev->iamthif_timer +
1461 msecs_to_jiffies(IAMTHIF_READ_TIMER);
1462
1463 dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n",
1464 dev->iamthif_timer);
1465 dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout);
1466 dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies);
1467 if (time_after(jiffies, timeout)) {
1468 /*
1469 * User didn't read the AMTHI data on time (15sec)
1470 * freeing AMTHI for other requests
1471 */
1472
1473 dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n");
1474
1475 amthi_complete_list = &dev->amthi_read_complete_list.
1476 mei_cb.cb_list;
1477
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001478 list_for_each_entry_safe(cb_pos, cb_next, amthi_complete_list, cb_list) {
Oren Weilfb7d8792011-05-15 13:43:42 +03001479
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001480 cl_pos = cb_pos->file_object->private_data;
Oren Weilfb7d8792011-05-15 13:43:42 +03001481
Tomas Winklerb7cd2d92011-11-27 21:43:34 +02001482 /* Finding the AMTHI entry. */
1483 if (cl_pos == &dev->iamthif_cl)
1484 list_del(&cb_pos->cb_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001485 }
1486 if (dev->iamthif_current_cb)
1487 mei_free_cb_private(dev->iamthif_current_cb);
1488
1489 dev->iamthif_file_object->private_data = NULL;
1490 dev->iamthif_file_object = NULL;
1491 dev->iamthif_current_cb = NULL;
1492 dev->iamthif_timer = 0;
Tomas Winklerc95efb72011-05-25 17:28:21 +03001493 mei_run_next_iamthif_cmd(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001494
1495 }
1496 }
1497out:
Tomas Winkler441ab502011-12-13 23:39:34 +02001498 schedule_delayed_work(&dev->timer_work, 2 * HZ);
1499 mutex_unlock(&dev->device_lock);
Oren Weilfb7d8792011-05-15 13:43:42 +03001500}
1501
1502/**
1503 * mei_interrupt_thread_handler - function called after ISR to handle the interrupt
1504 * processing.
1505 *
1506 * @irq: The irq number
1507 * @dev_id: pointer to the device structure
1508 *
1509 * returns irqreturn_t
1510 *
1511 */
1512irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
1513{
1514 struct mei_device *dev = (struct mei_device *) dev_id;
1515 struct mei_io_list complete_list;
1516 struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL;
1517 struct mei_cl *cl;
1518 s32 slots;
1519 int rets;
1520 bool bus_message_received;
1521
1522
1523 dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
1524 /* initialize our complete list */
1525 mutex_lock(&dev->device_lock);
Tomas Winkler0288c7c2011-06-06 10:44:34 +03001526 mei_io_list_init(&complete_list);
Oren Weilfb7d8792011-05-15 13:43:42 +03001527 dev->host_hw_state = mei_hcsr_read(dev);
Tomas Winkler4f61a7a2011-07-14 20:11:25 +03001528
1529 /* Ack the interrupt here
1530 * In case of MSI we don't go throuhg the quick handler */
1531 if (pci_dev_msi_enabled(dev->pdev))
1532 mei_reg_write(dev, H_CSR, dev->host_hw_state);
1533
Oren Weilfb7d8792011-05-15 13:43:42 +03001534 dev->me_hw_state = mei_mecsr_read(dev);
1535
1536 /* check if ME wants a reset */
1537 if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
1538 dev->mei_state != MEI_RESETING &&
1539 dev->mei_state != MEI_INITIALIZING) {
1540 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1541 mei_reset(dev, 1);
1542 mutex_unlock(&dev->device_lock);
1543 return IRQ_HANDLED;
1544 }
1545
1546 /* check if we need to start the dev */
1547 if ((dev->host_hw_state & H_RDY) == 0) {
1548 if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) {
1549 dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
1550 dev->host_hw_state |= (H_IE | H_IG | H_RDY);
1551 mei_hcsr_set(dev);
1552 dev->mei_state = MEI_INIT_CLIENTS;
1553 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
1554 /* link is established
1555 * start sending messages.
1556 */
Tomas Winklerc95efb72011-05-25 17:28:21 +03001557 mei_host_start_message(dev);
Oren Weilfb7d8792011-05-15 13:43:42 +03001558 mutex_unlock(&dev->device_lock);
1559 return IRQ_HANDLED;
1560 } else {
1561 dev_dbg(&dev->pdev->dev, "FW not ready.\n");
1562 mutex_unlock(&dev->device_lock);
1563 return IRQ_HANDLED;
1564 }
1565 }
1566 /* check slots avalable for reading */
1567 slots = mei_count_full_read_slots(dev);
1568 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1569 slots, dev->extra_write_index);
1570 while (slots > 0 && !dev->extra_write_index) {
1571 dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n",
1572 slots, dev->extra_write_index);
1573 dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n");
1574 rets = mei_irq_thread_read_handler(&complete_list, dev, &slots);
1575 if (rets)
1576 goto end;
1577 }
1578 rets = mei_irq_thread_write_handler(&complete_list, dev, &slots);
1579end:
1580 dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
1581 dev->host_hw_state = mei_hcsr_read(dev);
1582 dev->mei_host_buffer_is_empty = mei_host_buffer_is_empty(dev);
1583
1584 bus_message_received = false;
1585 if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) {
1586 dev_dbg(&dev->pdev->dev, "received waiting bus message\n");
1587 bus_message_received = true;
1588 }
1589 mutex_unlock(&dev->device_lock);
1590 if (bus_message_received) {
1591 dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n");
1592 wake_up_interruptible(&dev->wait_recvd_msg);
1593 bus_message_received = false;
1594 }
Tomas Winklerc8372092011-11-27 21:43:33 +02001595 if (list_empty(&complete_list.mei_cb.cb_list))
Oren Weilfb7d8792011-05-15 13:43:42 +03001596 return IRQ_HANDLED;
1597
1598
1599 list_for_each_entry_safe(cb_pos, cb_next,
1600 &complete_list.mei_cb.cb_list, cb_list) {
1601 cl = (struct mei_cl *)cb_pos->file_private;
1602 list_del(&cb_pos->cb_list);
1603 if (cl) {
1604 if (cl != &dev->iamthif_cl) {
1605 dev_dbg(&dev->pdev->dev, "completing call back.\n");
1606 _mei_cmpl(cl, cb_pos);
1607 cb_pos = NULL;
1608 } else if (cl == &dev->iamthif_cl) {
1609 _mei_cmpl_iamthif(dev, cb_pos);
1610 }
1611 }
1612 }
1613 return IRQ_HANDLED;
1614}