diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/staging/mei/interrupt.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/staging/mei/interrupt.c')
-rw-r--r-- | drivers/staging/mei/interrupt.c | 1623 |
1 files changed, 1623 insertions, 0 deletions
diff --git a/drivers/staging/mei/interrupt.c b/drivers/staging/mei/interrupt.c new file mode 100644 index 00000000000..9cb186bf187 --- /dev/null +++ b/drivers/staging/mei/interrupt.c | |||
@@ -0,0 +1,1623 @@ | |||
1 | /* | ||
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 | */ | ||
38 | irqreturn_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 | */ | ||
58 | static 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 | */ | ||
82 | static 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 { | ||
97 | mei_run_next_iamthif_cmd(dev); | ||
98 | } | ||
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 | */ | ||
115 | static 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 | */ | ||
173 | static 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 | */ | ||
192 | static 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; | ||
198 | unsigned char *buffer = NULL; | ||
199 | |||
200 | dev_dbg(&dev->pdev->dev, "start client msg\n"); | ||
201 | if (!(dev->read_list.status == 0 && | ||
202 | !list_empty(&dev->read_list.mei_cb.cb_list))) | ||
203 | goto quit; | ||
204 | |||
205 | list_for_each_entry_safe(cb_pos, cb_next, | ||
206 | &dev->read_list.mei_cb.cb_list, cb_list) { | ||
207 | cl = (struct mei_cl *)cb_pos->file_private; | ||
208 | if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { | ||
209 | cl->reading_state = MEI_READING; | ||
210 | buffer = (unsigned char *) | ||
211 | (cb_pos->response_buffer.data + | ||
212 | cb_pos->information); | ||
213 | BUG_ON(cb_pos->response_buffer.size < | ||
214 | mei_hdr->length + | ||
215 | cb_pos->information); | ||
216 | |||
217 | if (cb_pos->response_buffer.size < | ||
218 | mei_hdr->length + cb_pos->information) { | ||
219 | dev_dbg(&dev->pdev->dev, "message overflow.\n"); | ||
220 | list_del(&cb_pos->cb_list); | ||
221 | return -ENOMEM; | ||
222 | } | ||
223 | if (buffer) | ||
224 | mei_read_slots(dev, buffer, mei_hdr->length); | ||
225 | |||
226 | cb_pos->information += mei_hdr->length; | ||
227 | if (mei_hdr->msg_complete) { | ||
228 | cl->status = 0; | ||
229 | list_del(&cb_pos->cb_list); | ||
230 | dev_dbg(&dev->pdev->dev, | ||
231 | "completed read host client = %d," | ||
232 | "ME client = %d, " | ||
233 | "data length = %lu\n", | ||
234 | cl->host_client_id, | ||
235 | cl->me_client_id, | ||
236 | cb_pos->information); | ||
237 | |||
238 | *(cb_pos->response_buffer.data + | ||
239 | cb_pos->information) = '\0'; | ||
240 | dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n", | ||
241 | cb_pos->response_buffer.data); | ||
242 | list_add_tail(&cb_pos->cb_list, | ||
243 | &complete_list->mei_cb.cb_list); | ||
244 | } | ||
245 | |||
246 | break; | ||
247 | } | ||
248 | |||
249 | } | ||
250 | |||
251 | quit: | ||
252 | dev_dbg(&dev->pdev->dev, "message read\n"); | ||
253 | if (!buffer) { | ||
254 | mei_read_slots(dev, (unsigned char *) dev->rd_msg_buf, | ||
255 | mei_hdr->length); | ||
256 | dev_dbg(&dev->pdev->dev, "discarding message, header =%08x.\n", | ||
257 | *(u32 *) dev->rd_msg_buf); | ||
258 | } | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * _mei_irq_thread_iamthif_read - prepares to read iamthif data. | ||
265 | * | ||
266 | * @dev: the device structure. | ||
267 | * @slots: free slots. | ||
268 | * | ||
269 | * returns 0, OK; otherwise, error. | ||
270 | */ | ||
271 | static int _mei_irq_thread_iamthif_read(struct mei_device *dev, s32 *slots) | ||
272 | { | ||
273 | |||
274 | if (((*slots) * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) | ||
275 | + sizeof(struct hbm_flow_control))) { | ||
276 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
277 | sizeof(struct hbm_flow_control) + 3) / 4; | ||
278 | if (!mei_send_flow_control(dev, &dev->iamthif_cl)) { | ||
279 | dev_dbg(&dev->pdev->dev, "iamthif flow control failed\n"); | ||
280 | } else { | ||
281 | dev_dbg(&dev->pdev->dev, "iamthif flow control success\n"); | ||
282 | dev->iamthif_state = MEI_IAMTHIF_READING; | ||
283 | dev->iamthif_flow_control_pending = false; | ||
284 | dev->iamthif_msg_buf_index = 0; | ||
285 | dev->iamthif_msg_buf_size = 0; | ||
286 | dev->iamthif_stall_timer = IAMTHIF_STALL_TIMER; | ||
287 | dev->mei_host_buffer_is_empty = | ||
288 | mei_host_buffer_is_empty(dev); | ||
289 | } | ||
290 | return 0; | ||
291 | } else { | ||
292 | return -EMSGSIZE; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * _mei_irq_thread_close - processes close related operation. | ||
298 | * | ||
299 | * @dev: the device structure. | ||
300 | * @slots: free slots. | ||
301 | * @cb_pos: callback block. | ||
302 | * @cl: private data of the file object. | ||
303 | * @cmpl_list: complete list. | ||
304 | * | ||
305 | * returns 0, OK; otherwise, error. | ||
306 | */ | ||
307 | static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, | ||
308 | struct mei_cl_cb *cb_pos, | ||
309 | struct mei_cl *cl, | ||
310 | struct mei_io_list *cmpl_list) | ||
311 | { | ||
312 | if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + | ||
313 | sizeof(struct hbm_client_disconnect_request))) { | ||
314 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
315 | sizeof(struct hbm_client_disconnect_request) + 3) / 4; | ||
316 | |||
317 | if (!mei_disconnect(dev, cl)) { | ||
318 | cl->status = 0; | ||
319 | cb_pos->information = 0; | ||
320 | list_move_tail(&cb_pos->cb_list, | ||
321 | &cmpl_list->mei_cb.cb_list); | ||
322 | return -EMSGSIZE; | ||
323 | } else { | ||
324 | cl->state = MEI_FILE_DISCONNECTING; | ||
325 | cl->status = 0; | ||
326 | cb_pos->information = 0; | ||
327 | list_move_tail(&cb_pos->cb_list, | ||
328 | &dev->ctrl_rd_list.mei_cb.cb_list); | ||
329 | cl->timer_count = MEI_CONNECT_TIMEOUT; | ||
330 | } | ||
331 | } else { | ||
332 | /* return the cancel routine */ | ||
333 | return -EBADMSG; | ||
334 | } | ||
335 | |||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | /** | ||
340 | * is_treat_specially_client - checks if the message belongs | ||
341 | * to the file private data. | ||
342 | * | ||
343 | * @cl: private data of the file object | ||
344 | * @rs: connect response bus message | ||
345 | * | ||
346 | */ | ||
347 | static bool is_treat_specially_client(struct mei_cl *cl, | ||
348 | struct hbm_client_connect_response *rs) | ||
349 | { | ||
350 | |||
351 | if (cl->host_client_id == rs->host_addr && | ||
352 | cl->me_client_id == rs->me_addr) { | ||
353 | if (!rs->status) { | ||
354 | cl->state = MEI_FILE_CONNECTED; | ||
355 | cl->status = 0; | ||
356 | |||
357 | } else { | ||
358 | cl->state = MEI_FILE_DISCONNECTED; | ||
359 | cl->status = -ENODEV; | ||
360 | } | ||
361 | cl->timer_count = 0; | ||
362 | |||
363 | return true; | ||
364 | } | ||
365 | return false; | ||
366 | } | ||
367 | |||
368 | /** | ||
369 | * mei_client_connect_response - connects to response irq routine | ||
370 | * | ||
371 | * @dev: the device structure | ||
372 | * @rs: connect response bus message | ||
373 | */ | ||
374 | static void mei_client_connect_response(struct mei_device *dev, | ||
375 | struct hbm_client_connect_response *rs) | ||
376 | { | ||
377 | |||
378 | struct mei_cl *cl; | ||
379 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | ||
380 | |||
381 | dev_dbg(&dev->pdev->dev, | ||
382 | "connect_response:\n" | ||
383 | "ME Client = %d\n" | ||
384 | "Host Client = %d\n" | ||
385 | "Status = %d\n", | ||
386 | rs->me_addr, | ||
387 | rs->host_addr, | ||
388 | rs->status); | ||
389 | |||
390 | /* if WD or iamthif client treat specially */ | ||
391 | |||
392 | if (is_treat_specially_client(&(dev->wd_cl), rs)) { | ||
393 | dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n", | ||
394 | dev->wd_timeout); | ||
395 | |||
396 | dev->wd_due_counter = (dev->wd_timeout) ? 1 : 0; | ||
397 | |||
398 | dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); | ||
399 | mei_host_init_iamthif(dev); | ||
400 | return; | ||
401 | } | ||
402 | |||
403 | if (is_treat_specially_client(&(dev->iamthif_cl), rs)) { | ||
404 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
405 | return; | ||
406 | } | ||
407 | if (!dev->ctrl_rd_list.status && | ||
408 | !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) { | ||
409 | list_for_each_entry_safe(cb_pos, cb_next, | ||
410 | &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { | ||
411 | cl = (struct mei_cl *)cb_pos->file_private; | ||
412 | if (!cl) { | ||
413 | list_del(&cb_pos->cb_list); | ||
414 | return; | ||
415 | } | ||
416 | if (MEI_IOCTL == cb_pos->major_file_operations) { | ||
417 | if (is_treat_specially_client(cl, rs)) { | ||
418 | list_del(&cb_pos->cb_list); | ||
419 | cl->status = 0; | ||
420 | cl->timer_count = 0; | ||
421 | break; | ||
422 | } | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | |||
428 | /** | ||
429 | * mei_client_disconnect_response - disconnects from response irq routine | ||
430 | * | ||
431 | * @dev: the device structure | ||
432 | * @rs: disconnect response bus message | ||
433 | */ | ||
434 | static void mei_client_disconnect_response(struct mei_device *dev, | ||
435 | struct hbm_client_connect_response *rs) | ||
436 | { | ||
437 | struct mei_cl *cl; | ||
438 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | ||
439 | |||
440 | dev_dbg(&dev->pdev->dev, | ||
441 | "disconnect_response:\n" | ||
442 | "ME Client = %d\n" | ||
443 | "Host Client = %d\n" | ||
444 | "Status = %d\n", | ||
445 | rs->me_addr, | ||
446 | rs->host_addr, | ||
447 | rs->status); | ||
448 | |||
449 | if (!dev->ctrl_rd_list.status && | ||
450 | !list_empty(&dev->ctrl_rd_list.mei_cb.cb_list)) { | ||
451 | list_for_each_entry_safe(cb_pos, cb_next, | ||
452 | &dev->ctrl_rd_list.mei_cb.cb_list, cb_list) { | ||
453 | cl = (struct mei_cl *)cb_pos->file_private; | ||
454 | |||
455 | if (!cl) { | ||
456 | list_del(&cb_pos->cb_list); | ||
457 | return; | ||
458 | } | ||
459 | |||
460 | dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); | ||
461 | if (cl->host_client_id == rs->host_addr && | ||
462 | cl->me_client_id == rs->me_addr) { | ||
463 | |||
464 | list_del(&cb_pos->cb_list); | ||
465 | if (!rs->status) | ||
466 | cl->state = MEI_FILE_DISCONNECTED; | ||
467 | |||
468 | cl->status = 0; | ||
469 | cl->timer_count = 0; | ||
470 | break; | ||
471 | } | ||
472 | } | ||
473 | } | ||
474 | } | ||
475 | |||
476 | /** | ||
477 | * same_flow_addr - tells if they have the same address. | ||
478 | * | ||
479 | * @file: private data of the file object. | ||
480 | * @flow: flow control. | ||
481 | * | ||
482 | * returns !=0, same; 0,not. | ||
483 | */ | ||
484 | static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow) | ||
485 | { | ||
486 | return (cl->host_client_id == flow->host_addr && | ||
487 | cl->me_client_id == flow->me_addr); | ||
488 | } | ||
489 | |||
490 | /** | ||
491 | * add_single_flow_creds - adds single buffer credentials. | ||
492 | * | ||
493 | * @file: private data ot the file object. | ||
494 | * @flow: flow control. | ||
495 | */ | ||
496 | static void add_single_flow_creds(struct mei_device *dev, | ||
497 | struct hbm_flow_control *flow) | ||
498 | { | ||
499 | struct mei_me_client *client; | ||
500 | int i; | ||
501 | |||
502 | for (i = 0; i < dev->me_clients_num; i++) { | ||
503 | client = &dev->me_clients[i]; | ||
504 | if (client && flow->me_addr == client->client_id) { | ||
505 | if (client->props.single_recv_buf) { | ||
506 | client->mei_flow_ctrl_creds++; | ||
507 | dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", | ||
508 | flow->me_addr); | ||
509 | dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", | ||
510 | client->mei_flow_ctrl_creds); | ||
511 | } else { | ||
512 | BUG(); /* error in flow control */ | ||
513 | } | ||
514 | } | ||
515 | } | ||
516 | } | ||
517 | |||
518 | /** | ||
519 | * mei_client_flow_control_response - flow control response irq routine | ||
520 | * | ||
521 | * @dev: the device structure | ||
522 | * @flow_control: flow control response bus message | ||
523 | */ | ||
524 | static void mei_client_flow_control_response(struct mei_device *dev, | ||
525 | struct hbm_flow_control *flow_control) | ||
526 | { | ||
527 | struct mei_cl *cl_pos = NULL; | ||
528 | struct mei_cl *cl_next = NULL; | ||
529 | |||
530 | if (!flow_control->host_addr) { | ||
531 | /* single receive buffer */ | ||
532 | add_single_flow_creds(dev, flow_control); | ||
533 | } else { | ||
534 | /* normal connection */ | ||
535 | list_for_each_entry_safe(cl_pos, cl_next, | ||
536 | &dev->file_list, link) { | ||
537 | dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n"); | ||
538 | |||
539 | dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n", | ||
540 | cl_pos->host_client_id, | ||
541 | cl_pos->me_client_id); | ||
542 | dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", | ||
543 | flow_control->host_addr, | ||
544 | flow_control->me_addr); | ||
545 | if (same_flow_addr(cl_pos, flow_control)) { | ||
546 | dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n", | ||
547 | flow_control->host_addr, | ||
548 | flow_control->me_addr); | ||
549 | cl_pos->mei_flow_ctrl_creds++; | ||
550 | dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n", | ||
551 | cl_pos->mei_flow_ctrl_creds); | ||
552 | break; | ||
553 | } | ||
554 | } | ||
555 | } | ||
556 | } | ||
557 | |||
558 | /** | ||
559 | * same_disconn_addr - tells if they have the same address | ||
560 | * | ||
561 | * @file: private data of the file object. | ||
562 | * @disconn: disconnection request. | ||
563 | * | ||
564 | * returns !=0, same; 0,not. | ||
565 | */ | ||
566 | static int same_disconn_addr(struct mei_cl *cl, | ||
567 | struct hbm_client_disconnect_request *disconn) | ||
568 | { | ||
569 | return (cl->host_client_id == disconn->host_addr && | ||
570 | cl->me_client_id == disconn->me_addr); | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * mei_client_disconnect_request - disconnects from request irq routine | ||
575 | * | ||
576 | * @dev: the device structure. | ||
577 | * @disconnect_req: disconnect request bus message. | ||
578 | */ | ||
579 | static void mei_client_disconnect_request(struct mei_device *dev, | ||
580 | struct hbm_client_disconnect_request *disconnect_req) | ||
581 | { | ||
582 | struct mei_msg_hdr *mei_hdr; | ||
583 | struct hbm_client_connect_response *disconnect_res; | ||
584 | struct mei_cl *cl_pos = NULL; | ||
585 | struct mei_cl *cl_next = NULL; | ||
586 | |||
587 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | ||
588 | if (same_disconn_addr(cl_pos, disconnect_req)) { | ||
589 | dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", | ||
590 | disconnect_req->host_addr, | ||
591 | disconnect_req->me_addr); | ||
592 | cl_pos->state = MEI_FILE_DISCONNECTED; | ||
593 | cl_pos->timer_count = 0; | ||
594 | if (cl_pos == &dev->wd_cl) { | ||
595 | dev->wd_due_counter = 0; | ||
596 | dev->wd_pending = false; | ||
597 | } else if (cl_pos == &dev->iamthif_cl) | ||
598 | dev->iamthif_timer = 0; | ||
599 | |||
600 | /* prepare disconnect response */ | ||
601 | mei_hdr = | ||
602 | (struct mei_msg_hdr *) &dev->ext_msg_buf[0]; | ||
603 | mei_hdr->host_addr = 0; | ||
604 | mei_hdr->me_addr = 0; | ||
605 | mei_hdr->length = | ||
606 | sizeof(struct hbm_client_connect_response); | ||
607 | mei_hdr->msg_complete = 1; | ||
608 | mei_hdr->reserved = 0; | ||
609 | |||
610 | disconnect_res = | ||
611 | (struct hbm_client_connect_response *) | ||
612 | &dev->ext_msg_buf[1]; | ||
613 | disconnect_res->host_addr = cl_pos->host_client_id; | ||
614 | disconnect_res->me_addr = cl_pos->me_client_id; | ||
615 | *(u8 *) (&disconnect_res->cmd) = | ||
616 | CLIENT_DISCONNECT_RES_CMD; | ||
617 | disconnect_res->status = 0; | ||
618 | dev->extra_write_index = 2; | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | } | ||
623 | |||
624 | |||
625 | /** | ||
626 | * mei_irq_thread_read_bus_message - bottom half read routine after ISR to | ||
627 | * handle the read bus message cmd processing. | ||
628 | * | ||
629 | * @dev: the device structure | ||
630 | * @mei_hdr: header of bus message | ||
631 | */ | ||
632 | static void mei_irq_thread_read_bus_message(struct mei_device *dev, | ||
633 | struct mei_msg_hdr *mei_hdr) | ||
634 | { | ||
635 | struct mei_bus_message *mei_msg; | ||
636 | struct hbm_host_version_response *version_res; | ||
637 | struct hbm_client_connect_response *connect_res; | ||
638 | struct hbm_client_connect_response *disconnect_res; | ||
639 | struct hbm_flow_control *flow_control; | ||
640 | struct hbm_props_response *props_res; | ||
641 | struct hbm_host_enum_response *enum_res; | ||
642 | struct hbm_client_disconnect_request *disconnect_req; | ||
643 | struct hbm_host_stop_request *host_stop_req; | ||
644 | |||
645 | unsigned char *buffer; | ||
646 | |||
647 | /* read the message to our buffer */ | ||
648 | buffer = (unsigned char *) dev->rd_msg_buf; | ||
649 | BUG_ON(mei_hdr->length >= sizeof(dev->rd_msg_buf)); | ||
650 | mei_read_slots(dev, buffer, mei_hdr->length); | ||
651 | mei_msg = (struct mei_bus_message *) buffer; | ||
652 | |||
653 | switch (*(u8 *) mei_msg) { | ||
654 | case HOST_START_RES_CMD: | ||
655 | version_res = (struct hbm_host_version_response *) mei_msg; | ||
656 | if (version_res->host_version_supported) { | ||
657 | dev->version.major_version = HBM_MAJOR_VERSION; | ||
658 | dev->version.minor_version = HBM_MINOR_VERSION; | ||
659 | if (dev->mei_state == MEI_INIT_CLIENTS && | ||
660 | dev->init_clients_state == MEI_START_MESSAGE) { | ||
661 | dev->init_clients_timer = 0; | ||
662 | mei_host_enum_clients_message(dev); | ||
663 | } else { | ||
664 | dev->recvd_msg = false; | ||
665 | dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n"); | ||
666 | mei_reset(dev, 1); | ||
667 | return; | ||
668 | } | ||
669 | } else { | ||
670 | dev->version = version_res->me_max_version; | ||
671 | /* send stop message */ | ||
672 | mei_hdr->host_addr = 0; | ||
673 | mei_hdr->me_addr = 0; | ||
674 | mei_hdr->length = sizeof(struct hbm_host_stop_request); | ||
675 | mei_hdr->msg_complete = 1; | ||
676 | mei_hdr->reserved = 0; | ||
677 | |||
678 | host_stop_req = (struct hbm_host_stop_request *) | ||
679 | &dev->wr_msg_buf[1]; | ||
680 | |||
681 | memset(host_stop_req, | ||
682 | 0, | ||
683 | sizeof(struct hbm_host_stop_request)); | ||
684 | host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD; | ||
685 | host_stop_req->reason = DRIVER_STOP_REQUEST; | ||
686 | mei_write_message(dev, mei_hdr, | ||
687 | (unsigned char *) (host_stop_req), | ||
688 | mei_hdr->length); | ||
689 | dev_dbg(&dev->pdev->dev, "version mismatch.\n"); | ||
690 | return; | ||
691 | } | ||
692 | |||
693 | dev->recvd_msg = true; | ||
694 | dev_dbg(&dev->pdev->dev, "host start response message received.\n"); | ||
695 | break; | ||
696 | |||
697 | case CLIENT_CONNECT_RES_CMD: | ||
698 | connect_res = | ||
699 | (struct hbm_client_connect_response *) mei_msg; | ||
700 | mei_client_connect_response(dev, connect_res); | ||
701 | dev_dbg(&dev->pdev->dev, "client connect response message received.\n"); | ||
702 | wake_up(&dev->wait_recvd_msg); | ||
703 | break; | ||
704 | |||
705 | case CLIENT_DISCONNECT_RES_CMD: | ||
706 | disconnect_res = | ||
707 | (struct hbm_client_connect_response *) mei_msg; | ||
708 | mei_client_disconnect_response(dev, disconnect_res); | ||
709 | dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n"); | ||
710 | wake_up(&dev->wait_recvd_msg); | ||
711 | break; | ||
712 | |||
713 | case MEI_FLOW_CONTROL_CMD: | ||
714 | flow_control = (struct hbm_flow_control *) mei_msg; | ||
715 | mei_client_flow_control_response(dev, flow_control); | ||
716 | dev_dbg(&dev->pdev->dev, "client flow control response message received.\n"); | ||
717 | break; | ||
718 | |||
719 | case HOST_CLIENT_PROPERTIES_RES_CMD: | ||
720 | props_res = (struct hbm_props_response *)mei_msg; | ||
721 | if (props_res->status || !dev->me_clients) { | ||
722 | dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n"); | ||
723 | mei_reset(dev, 1); | ||
724 | return; | ||
725 | } | ||
726 | if (dev->me_clients[dev->me_client_presentation_num] | ||
727 | .client_id == props_res->address) { | ||
728 | |||
729 | dev->me_clients[dev->me_client_presentation_num].props | ||
730 | = props_res->client_properties; | ||
731 | |||
732 | if (dev->mei_state == MEI_INIT_CLIENTS && | ||
733 | dev->init_clients_state == | ||
734 | MEI_CLIENT_PROPERTIES_MESSAGE) { | ||
735 | dev->me_client_index++; | ||
736 | dev->me_client_presentation_num++; | ||
737 | mei_host_client_properties(dev); | ||
738 | } else { | ||
739 | dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message"); | ||
740 | mei_reset(dev, 1); | ||
741 | return; | ||
742 | } | ||
743 | } else { | ||
744 | dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message for wrong client ID\n"); | ||
745 | mei_reset(dev, 1); | ||
746 | return; | ||
747 | } | ||
748 | break; | ||
749 | |||
750 | case HOST_ENUM_RES_CMD: | ||
751 | enum_res = (struct hbm_host_enum_response *) mei_msg; | ||
752 | memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); | ||
753 | if (dev->mei_state == MEI_INIT_CLIENTS && | ||
754 | dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) { | ||
755 | dev->init_clients_timer = 0; | ||
756 | dev->me_client_presentation_num = 0; | ||
757 | dev->me_client_index = 0; | ||
758 | mei_allocate_me_clients_storage(dev); | ||
759 | dev->init_clients_state = | ||
760 | MEI_CLIENT_PROPERTIES_MESSAGE; | ||
761 | mei_host_client_properties(dev); | ||
762 | } else { | ||
763 | dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n"); | ||
764 | mei_reset(dev, 1); | ||
765 | return; | ||
766 | } | ||
767 | break; | ||
768 | |||
769 | case HOST_STOP_RES_CMD: | ||
770 | dev->mei_state = MEI_DISABLED; | ||
771 | dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n"); | ||
772 | mei_reset(dev, 1); | ||
773 | break; | ||
774 | |||
775 | case CLIENT_DISCONNECT_REQ_CMD: | ||
776 | /* search for client */ | ||
777 | disconnect_req = | ||
778 | (struct hbm_client_disconnect_request *) mei_msg; | ||
779 | mei_client_disconnect_request(dev, disconnect_req); | ||
780 | break; | ||
781 | |||
782 | case ME_STOP_REQ_CMD: | ||
783 | /* prepare stop request */ | ||
784 | mei_hdr = (struct mei_msg_hdr *) &dev->ext_msg_buf[0]; | ||
785 | mei_hdr->host_addr = 0; | ||
786 | mei_hdr->me_addr = 0; | ||
787 | mei_hdr->length = sizeof(struct hbm_host_stop_request); | ||
788 | mei_hdr->msg_complete = 1; | ||
789 | mei_hdr->reserved = 0; | ||
790 | host_stop_req = | ||
791 | (struct hbm_host_stop_request *) &dev->ext_msg_buf[1]; | ||
792 | memset(host_stop_req, 0, sizeof(struct hbm_host_stop_request)); | ||
793 | host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD; | ||
794 | host_stop_req->reason = DRIVER_STOP_REQUEST; | ||
795 | host_stop_req->reserved[0] = 0; | ||
796 | host_stop_req->reserved[1] = 0; | ||
797 | dev->extra_write_index = 2; | ||
798 | break; | ||
799 | |||
800 | default: | ||
801 | BUG(); | ||
802 | break; | ||
803 | |||
804 | } | ||
805 | } | ||
806 | |||
807 | |||
808 | /** | ||
809 | * _mei_hb_read - processes read related operation. | ||
810 | * | ||
811 | * @dev: the device structure. | ||
812 | * @slots: free slots. | ||
813 | * @cb_pos: callback block. | ||
814 | * @cl: private data of the file object. | ||
815 | * @cmpl_list: complete list. | ||
816 | * | ||
817 | * returns 0, OK; otherwise, error. | ||
818 | */ | ||
819 | static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, | ||
820 | struct mei_cl_cb *cb_pos, | ||
821 | struct mei_cl *cl, | ||
822 | struct mei_io_list *cmpl_list) | ||
823 | { | ||
824 | if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + | ||
825 | sizeof(struct hbm_flow_control))) { | ||
826 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
827 | sizeof(struct hbm_flow_control) + 3) / 4; | ||
828 | if (!mei_send_flow_control(dev, cl)) { | ||
829 | cl->status = -ENODEV; | ||
830 | cb_pos->information = 0; | ||
831 | list_move_tail(&cb_pos->cb_list, | ||
832 | &cmpl_list->mei_cb.cb_list); | ||
833 | return -ENODEV; | ||
834 | } else { | ||
835 | list_move_tail(&cb_pos->cb_list, | ||
836 | &dev->read_list.mei_cb.cb_list); | ||
837 | } | ||
838 | } else { | ||
839 | /* return the cancel routine */ | ||
840 | list_del(&cb_pos->cb_list); | ||
841 | return -EBADMSG; | ||
842 | } | ||
843 | |||
844 | return 0; | ||
845 | } | ||
846 | |||
847 | |||
848 | /** | ||
849 | * _mei_irq_thread_ioctl - processes ioctl related operation. | ||
850 | * | ||
851 | * @dev: the device structure. | ||
852 | * @slots: free slots. | ||
853 | * @cb_pos: callback block. | ||
854 | * @cl: private data of the file object. | ||
855 | * @cmpl_list: complete list. | ||
856 | * | ||
857 | * returns 0, OK; otherwise, error. | ||
858 | */ | ||
859 | static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, | ||
860 | struct mei_cl_cb *cb_pos, | ||
861 | struct mei_cl *cl, | ||
862 | struct mei_io_list *cmpl_list) | ||
863 | { | ||
864 | if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + | ||
865 | sizeof(struct hbm_client_connect_request))) { | ||
866 | cl->state = MEI_FILE_CONNECTING; | ||
867 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
868 | sizeof(struct hbm_client_connect_request) + 3) / 4; | ||
869 | if (!mei_connect(dev, cl)) { | ||
870 | cl->status = -ENODEV; | ||
871 | cb_pos->information = 0; | ||
872 | list_del(&cb_pos->cb_list); | ||
873 | return -ENODEV; | ||
874 | } else { | ||
875 | list_move_tail(&cb_pos->cb_list, | ||
876 | &dev->ctrl_rd_list.mei_cb.cb_list); | ||
877 | cl->timer_count = MEI_CONNECT_TIMEOUT; | ||
878 | } | ||
879 | } else { | ||
880 | /* return the cancel routine */ | ||
881 | list_del(&cb_pos->cb_list); | ||
882 | return -EBADMSG; | ||
883 | } | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | /** | ||
889 | * _mei_irq_thread_cmpl - processes completed and no-iamthif operation. | ||
890 | * | ||
891 | * @dev: the device structure. | ||
892 | * @slots: free slots. | ||
893 | * @cb_pos: callback block. | ||
894 | * @cl: private data of the file object. | ||
895 | * @cmpl_list: complete list. | ||
896 | * | ||
897 | * returns 0, OK; otherwise, error. | ||
898 | */ | ||
899 | static int _mei_irq_thread_cmpl(struct mei_device *dev, s32 *slots, | ||
900 | struct mei_cl_cb *cb_pos, | ||
901 | struct mei_cl *cl, | ||
902 | struct mei_io_list *cmpl_list) | ||
903 | { | ||
904 | struct mei_msg_hdr *mei_hdr; | ||
905 | |||
906 | if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + | ||
907 | (cb_pos->request_buffer.size - | ||
908 | cb_pos->information))) { | ||
909 | mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | ||
910 | mei_hdr->host_addr = cl->host_client_id; | ||
911 | mei_hdr->me_addr = cl->me_client_id; | ||
912 | mei_hdr->length = cb_pos->request_buffer.size - | ||
913 | cb_pos->information; | ||
914 | mei_hdr->msg_complete = 1; | ||
915 | mei_hdr->reserved = 0; | ||
916 | dev_dbg(&dev->pdev->dev, "cb_pos->request_buffer.size =%d" | ||
917 | "mei_hdr->msg_complete = %d\n", | ||
918 | cb_pos->request_buffer.size, | ||
919 | mei_hdr->msg_complete); | ||
920 | dev_dbg(&dev->pdev->dev, "cb_pos->information =%lu\n", | ||
921 | cb_pos->information); | ||
922 | dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", | ||
923 | mei_hdr->length); | ||
924 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
925 | mei_hdr->length + 3) / 4; | ||
926 | if (!mei_write_message(dev, mei_hdr, | ||
927 | (unsigned char *) | ||
928 | (cb_pos->request_buffer.data + | ||
929 | cb_pos->information), | ||
930 | mei_hdr->length)) { | ||
931 | cl->status = -ENODEV; | ||
932 | list_move_tail(&cb_pos->cb_list, | ||
933 | &cmpl_list->mei_cb.cb_list); | ||
934 | return -ENODEV; | ||
935 | } else { | ||
936 | if (mei_flow_ctrl_reduce(dev, cl)) | ||
937 | return -ENODEV; | ||
938 | cl->status = 0; | ||
939 | cb_pos->information += mei_hdr->length; | ||
940 | list_move_tail(&cb_pos->cb_list, | ||
941 | &dev->write_waiting_list.mei_cb.cb_list); | ||
942 | } | ||
943 | } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) { | ||
944 | /* buffer is still empty */ | ||
945 | mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | ||
946 | mei_hdr->host_addr = cl->host_client_id; | ||
947 | mei_hdr->me_addr = cl->me_client_id; | ||
948 | mei_hdr->length = | ||
949 | (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | ||
950 | mei_hdr->msg_complete = 0; | ||
951 | mei_hdr->reserved = 0; | ||
952 | |||
953 | (*slots) -= (sizeof(struct mei_msg_hdr) + | ||
954 | mei_hdr->length + 3) / 4; | ||
955 | if (!mei_write_message(dev, mei_hdr, | ||
956 | (unsigned char *) | ||
957 | (cb_pos->request_buffer.data + | ||
958 | cb_pos->information), | ||
959 | mei_hdr->length)) { | ||
960 | cl->status = -ENODEV; | ||
961 | list_move_tail(&cb_pos->cb_list, | ||
962 | &cmpl_list->mei_cb.cb_list); | ||
963 | return -ENODEV; | ||
964 | } else { | ||
965 | cb_pos->information += mei_hdr->length; | ||
966 | dev_dbg(&dev->pdev->dev, | ||
967 | "cb_pos->request_buffer.size =%d" | ||
968 | " mei_hdr->msg_complete = %d\n", | ||
969 | cb_pos->request_buffer.size, | ||
970 | mei_hdr->msg_complete); | ||
971 | dev_dbg(&dev->pdev->dev, "cb_pos->information =%lu\n", | ||
972 | cb_pos->information); | ||
973 | dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", | ||
974 | mei_hdr->length); | ||
975 | } | ||
976 | return -EMSGSIZE; | ||
977 | } else { | ||
978 | return -EBADMSG; | ||
979 | } | ||
980 | |||
981 | return 0; | ||
982 | } | ||
983 | |||
984 | /** | ||
985 | * _mei_irq_thread_cmpl_iamthif - processes completed iamthif operation. | ||
986 | * | ||
987 | * @dev: the device structure. | ||
988 | * @slots: free slots. | ||
989 | * @cb_pos: callback block. | ||
990 | * @cl: private data of the file object. | ||
991 | * @cmpl_list: complete list. | ||
992 | * | ||
993 | * returns 0, OK; otherwise, error. | ||
994 | */ | ||
995 | static int _mei_irq_thread_cmpl_iamthif(struct mei_device *dev, s32 *slots, | ||
996 | struct mei_cl_cb *cb_pos, | ||
997 | struct mei_cl *cl, | ||
998 | struct mei_io_list *cmpl_list) | ||
999 | { | ||
1000 | struct mei_msg_hdr *mei_hdr; | ||
1001 | |||
1002 | if ((*slots * sizeof(u32)) >= (sizeof(struct mei_msg_hdr) + | ||
1003 | dev->iamthif_msg_buf_size - | ||
1004 | dev->iamthif_msg_buf_index)) { | ||
1005 | mei_hdr = (struct mei_msg_hdr *) &dev->wr_msg_buf[0]; | ||
1006 | mei_hdr->host_addr = cl->host_client_id; | ||
1007 | mei_hdr->me_addr = cl->me_client_id; | ||
1008 | mei_hdr->length = dev->iamthif_msg_buf_size - | ||
1009 | dev->iamthif_msg_buf_index; | ||
1010 | mei_hdr->msg_complete = 1; | ||
1011 | mei_hdr->reserved = 0; | ||
1012 | |||
1013 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
1014 | mei_hdr->length + 3) / 4; | ||
1015 | |||
1016 | if (!mei_write_message(dev, mei_hdr, | ||
1017 | (dev->iamthif_msg_buf + | ||
1018 | dev->iamthif_msg_buf_index), | ||
1019 | mei_hdr->length)) { | ||
1020 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
1021 | cl->status = -ENODEV; | ||
1022 | list_del(&cb_pos->cb_list); | ||
1023 | return -ENODEV; | ||
1024 | } else { | ||
1025 | if (mei_flow_ctrl_reduce(dev, cl)) | ||
1026 | return -ENODEV; | ||
1027 | dev->iamthif_msg_buf_index += mei_hdr->length; | ||
1028 | cb_pos->information = dev->iamthif_msg_buf_index; | ||
1029 | cl->status = 0; | ||
1030 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; | ||
1031 | dev->iamthif_flow_control_pending = true; | ||
1032 | /* save iamthif cb sent to amthi client */ | ||
1033 | dev->iamthif_current_cb = cb_pos; | ||
1034 | list_move_tail(&cb_pos->cb_list, | ||
1035 | &dev->write_waiting_list.mei_cb.cb_list); | ||
1036 | |||
1037 | } | ||
1038 | } else if (*slots == ((dev->host_hw_state & H_CBD) >> 24)) { | ||
1039 | /* buffer is still empty */ | ||
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 = | ||
1044 | (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | ||
1045 | mei_hdr->msg_complete = 0; | ||
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 | cl->status = -ENODEV; | ||
1056 | list_del(&cb_pos->cb_list); | ||
1057 | } else { | ||
1058 | dev->iamthif_msg_buf_index += mei_hdr->length; | ||
1059 | } | ||
1060 | return -EMSGSIZE; | ||
1061 | } else { | ||
1062 | return -EBADMSG; | ||
1063 | } | ||
1064 | |||
1065 | return 0; | ||
1066 | } | ||
1067 | |||
1068 | /** | ||
1069 | * mei_irq_thread_read_handler - bottom half read routine after ISR to | ||
1070 | * handle the read processing. | ||
1071 | * | ||
1072 | * @cmpl_list: An instance of our list structure | ||
1073 | * @dev: the device structure | ||
1074 | * @slots: slots to read. | ||
1075 | * | ||
1076 | * returns 0 on success, <0 on failure. | ||
1077 | */ | ||
1078 | static int mei_irq_thread_read_handler(struct mei_io_list *cmpl_list, | ||
1079 | struct mei_device *dev, | ||
1080 | s32 *slots) | ||
1081 | { | ||
1082 | struct mei_msg_hdr *mei_hdr; | ||
1083 | struct mei_cl *cl_pos = NULL; | ||
1084 | struct mei_cl *cl_next = NULL; | ||
1085 | int ret = 0; | ||
1086 | |||
1087 | if (!dev->rd_msg_hdr) { | ||
1088 | dev->rd_msg_hdr = mei_mecbrw_read(dev); | ||
1089 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | ||
1090 | (*slots)--; | ||
1091 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | ||
1092 | } | ||
1093 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | ||
1094 | dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", mei_hdr->length); | ||
1095 | |||
1096 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | ||
1097 | dev_dbg(&dev->pdev->dev, "corrupted message header.\n"); | ||
1098 | ret = -EBADMSG; | ||
1099 | goto end; | ||
1100 | } | ||
1101 | |||
1102 | if (mei_hdr->host_addr || mei_hdr->me_addr) { | ||
1103 | list_for_each_entry_safe(cl_pos, cl_next, | ||
1104 | &dev->file_list, link) { | ||
1105 | dev_dbg(&dev->pdev->dev, | ||
1106 | "list_for_each_entry_safe read host" | ||
1107 | " client = %d, ME client = %d\n", | ||
1108 | cl_pos->host_client_id, | ||
1109 | cl_pos->me_client_id); | ||
1110 | if (cl_pos->host_client_id == mei_hdr->host_addr && | ||
1111 | cl_pos->me_client_id == mei_hdr->me_addr) | ||
1112 | break; | ||
1113 | } | ||
1114 | |||
1115 | if (&cl_pos->link == &dev->file_list) { | ||
1116 | dev_dbg(&dev->pdev->dev, "corrupted message header\n"); | ||
1117 | ret = -EBADMSG; | ||
1118 | goto end; | ||
1119 | } | ||
1120 | } | ||
1121 | if (((*slots) * sizeof(u32)) < mei_hdr->length) { | ||
1122 | dev_dbg(&dev->pdev->dev, | ||
1123 | "we can't read the message slots =%08x.\n", | ||
1124 | *slots); | ||
1125 | /* we can't read the message */ | ||
1126 | ret = -ERANGE; | ||
1127 | goto end; | ||
1128 | } | ||
1129 | |||
1130 | /* decide where to read the message too */ | ||
1131 | if (!mei_hdr->host_addr) { | ||
1132 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); | ||
1133 | mei_irq_thread_read_bus_message(dev, mei_hdr); | ||
1134 | dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); | ||
1135 | } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && | ||
1136 | (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && | ||
1137 | (dev->iamthif_state == MEI_IAMTHIF_READING)) { | ||
1138 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); | ||
1139 | dev_dbg(&dev->pdev->dev, "mei_hdr->length =%d\n", | ||
1140 | mei_hdr->length); | ||
1141 | ret = mei_irq_thread_read_amthi_message(cmpl_list, | ||
1142 | dev, mei_hdr); | ||
1143 | if (ret) | ||
1144 | goto end; | ||
1145 | |||
1146 | } else { | ||
1147 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); | ||
1148 | ret = mei_irq_thread_read_client_message(cmpl_list, | ||
1149 | dev, mei_hdr); | ||
1150 | if (ret) | ||
1151 | goto end; | ||
1152 | |||
1153 | } | ||
1154 | |||
1155 | /* reset the number of slots and header */ | ||
1156 | *slots = mei_count_full_read_slots(dev); | ||
1157 | dev->rd_msg_hdr = 0; | ||
1158 | |||
1159 | if (*slots == -EOVERFLOW) { | ||
1160 | /* overflow - reset */ | ||
1161 | dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); | ||
1162 | /* set the event since message has been read */ | ||
1163 | ret = -ERANGE; | ||
1164 | goto end; | ||
1165 | } | ||
1166 | end: | ||
1167 | return ret; | ||
1168 | } | ||
1169 | |||
1170 | |||
1171 | /** | ||
1172 | * mei_irq_thread_write_handler - bottom half write routine after | ||
1173 | * ISR to handle the write processing. | ||
1174 | * | ||
1175 | * @cmpl_list: An instance of our list structure | ||
1176 | * @dev: the device structure | ||
1177 | * @slots: slots to write. | ||
1178 | * | ||
1179 | * returns 0 on success, <0 on failure. | ||
1180 | */ | ||
1181 | static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, | ||
1182 | struct mei_device *dev, | ||
1183 | s32 *slots) | ||
1184 | { | ||
1185 | |||
1186 | struct mei_cl *cl; | ||
1187 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | ||
1188 | struct mei_io_list *list; | ||
1189 | int ret; | ||
1190 | |||
1191 | if (!mei_host_buffer_is_empty(dev)) { | ||
1192 | dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); | ||
1193 | return 0; | ||
1194 | } | ||
1195 | *slots = mei_count_empty_write_slots(dev); | ||
1196 | /* complete all waiting for write CB */ | ||
1197 | dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); | ||
1198 | |||
1199 | list = &dev->write_waiting_list; | ||
1200 | if (!list->status && !list_empty(&list->mei_cb.cb_list)) { | ||
1201 | list_for_each_entry_safe(cb_pos, cb_next, | ||
1202 | &list->mei_cb.cb_list, cb_list) { | ||
1203 | cl = (struct mei_cl *)cb_pos->file_private; | ||
1204 | if (cl) { | ||
1205 | cl->status = 0; | ||
1206 | list_del(&cb_pos->cb_list); | ||
1207 | if (MEI_WRITING == cl->writing_state && | ||
1208 | (cb_pos->major_file_operations == | ||
1209 | MEI_WRITE) && | ||
1210 | (cl != &dev->iamthif_cl)) { | ||
1211 | dev_dbg(&dev->pdev->dev, | ||
1212 | "MEI WRITE COMPLETE\n"); | ||
1213 | cl->writing_state = | ||
1214 | MEI_WRITE_COMPLETE; | ||
1215 | list_add_tail(&cb_pos->cb_list, | ||
1216 | &cmpl_list->mei_cb.cb_list); | ||
1217 | } | ||
1218 | if (cl == &dev->iamthif_cl) { | ||
1219 | dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); | ||
1220 | if (dev->iamthif_flow_control_pending) { | ||
1221 | ret = | ||
1222 | _mei_irq_thread_iamthif_read( | ||
1223 | dev, slots); | ||
1224 | if (ret) | ||
1225 | return ret; | ||
1226 | } | ||
1227 | } | ||
1228 | } | ||
1229 | |||
1230 | } | ||
1231 | } | ||
1232 | |||
1233 | if (dev->stop && !dev->wd_pending) { | ||
1234 | dev->wd_stopped = true; | ||
1235 | wake_up_interruptible(&dev->wait_stop_wd); | ||
1236 | return 0; | ||
1237 | } | ||
1238 | |||
1239 | if (dev->extra_write_index) { | ||
1240 | dev_dbg(&dev->pdev->dev, "extra_write_index =%d.\n", | ||
1241 | dev->extra_write_index); | ||
1242 | mei_write_message(dev, | ||
1243 | (struct mei_msg_hdr *) &dev->ext_msg_buf[0], | ||
1244 | (unsigned char *) &dev->ext_msg_buf[1], | ||
1245 | (dev->extra_write_index - 1) * sizeof(u32)); | ||
1246 | *slots -= dev->extra_write_index; | ||
1247 | dev->extra_write_index = 0; | ||
1248 | } | ||
1249 | if (dev->mei_state == MEI_ENABLED) { | ||
1250 | if (dev->wd_pending && | ||
1251 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { | ||
1252 | if (mei_wd_send(dev)) | ||
1253 | dev_dbg(&dev->pdev->dev, "wd send failed.\n"); | ||
1254 | else | ||
1255 | if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) | ||
1256 | return -ENODEV; | ||
1257 | |||
1258 | dev->wd_pending = false; | ||
1259 | |||
1260 | if (dev->wd_timeout) { | ||
1261 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
1262 | MEI_START_WD_DATA_SIZE + 3) / 4; | ||
1263 | dev->wd_due_counter = 2; | ||
1264 | } else { | ||
1265 | *slots -= (sizeof(struct mei_msg_hdr) + | ||
1266 | MEI_WD_PARAMS_SIZE + 3) / 4; | ||
1267 | dev->wd_due_counter = 0; | ||
1268 | } | ||
1269 | |||
1270 | } | ||
1271 | } | ||
1272 | if (dev->stop) | ||
1273 | return ~ENODEV; | ||
1274 | |||
1275 | /* complete control write list CB */ | ||
1276 | if (!dev->ctrl_wr_list.status) { | ||
1277 | /* complete control write list CB */ | ||
1278 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); | ||
1279 | list_for_each_entry_safe(cb_pos, cb_next, | ||
1280 | &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) { | ||
1281 | cl = (struct mei_cl *) | ||
1282 | cb_pos->file_private; | ||
1283 | if (!cl) { | ||
1284 | list_del(&cb_pos->cb_list); | ||
1285 | return -ENODEV; | ||
1286 | } | ||
1287 | switch (cb_pos->major_file_operations) { | ||
1288 | case MEI_CLOSE: | ||
1289 | /* send disconnect message */ | ||
1290 | ret = _mei_irq_thread_close(dev, slots, | ||
1291 | cb_pos, cl, cmpl_list); | ||
1292 | if (ret) | ||
1293 | return ret; | ||
1294 | |||
1295 | break; | ||
1296 | case MEI_READ: | ||
1297 | /* send flow control message */ | ||
1298 | ret = _mei_irq_thread_read(dev, slots, | ||
1299 | cb_pos, cl, cmpl_list); | ||
1300 | if (ret) | ||
1301 | return ret; | ||
1302 | |||
1303 | break; | ||
1304 | case MEI_IOCTL: | ||
1305 | /* connect message */ | ||
1306 | if (!mei_other_client_is_connecting(dev, | ||
1307 | cl)) | ||
1308 | continue; | ||
1309 | ret = _mei_irq_thread_ioctl(dev, slots, | ||
1310 | cb_pos, cl, cmpl_list); | ||
1311 | if (ret) | ||
1312 | return ret; | ||
1313 | |||
1314 | break; | ||
1315 | |||
1316 | default: | ||
1317 | BUG(); | ||
1318 | } | ||
1319 | |||
1320 | } | ||
1321 | } | ||
1322 | /* complete write list CB */ | ||
1323 | if (!dev->write_list.status && | ||
1324 | !list_empty(&dev->write_list.mei_cb.cb_list)) { | ||
1325 | dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); | ||
1326 | list_for_each_entry_safe(cb_pos, cb_next, | ||
1327 | &dev->write_list.mei_cb.cb_list, cb_list) { | ||
1328 | cl = (struct mei_cl *)cb_pos->file_private; | ||
1329 | |||
1330 | if (cl) { | ||
1331 | if (cl != &dev->iamthif_cl) { | ||
1332 | if (!mei_flow_ctrl_creds(dev, | ||
1333 | cl)) { | ||
1334 | dev_dbg(&dev->pdev->dev, | ||
1335 | "No flow control" | ||
1336 | " credentials for client" | ||
1337 | " %d, not sending.\n", | ||
1338 | cl->host_client_id); | ||
1339 | continue; | ||
1340 | } | ||
1341 | ret = _mei_irq_thread_cmpl(dev, slots, | ||
1342 | cb_pos, | ||
1343 | cl, cmpl_list); | ||
1344 | if (ret) | ||
1345 | return ret; | ||
1346 | |||
1347 | } else if (cl == &dev->iamthif_cl) { | ||
1348 | /* IAMTHIF IOCTL */ | ||
1349 | dev_dbg(&dev->pdev->dev, "complete amthi write cb.\n"); | ||
1350 | if (!mei_flow_ctrl_creds(dev, | ||
1351 | cl)) { | ||
1352 | dev_dbg(&dev->pdev->dev, | ||
1353 | "No flow control" | ||
1354 | " credentials for amthi" | ||
1355 | " client %d.\n", | ||
1356 | cl->host_client_id); | ||
1357 | continue; | ||
1358 | } | ||
1359 | ret = _mei_irq_thread_cmpl_iamthif(dev, | ||
1360 | slots, | ||
1361 | cb_pos, | ||
1362 | cl, | ||
1363 | cmpl_list); | ||
1364 | if (ret) | ||
1365 | return ret; | ||
1366 | |||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | } | ||
1371 | } | ||
1372 | return 0; | ||
1373 | } | ||
1374 | |||
1375 | |||
1376 | |||
1377 | /** | ||
1378 | * mei_timer - timer function. | ||
1379 | * | ||
1380 | * @work: pointer to the work_struct structure | ||
1381 | * | ||
1382 | * NOTE: This function is called by timer interrupt work | ||
1383 | */ | ||
1384 | void mei_wd_timer(struct work_struct *work) | ||
1385 | { | ||
1386 | unsigned long timeout; | ||
1387 | struct mei_cl *cl_pos = NULL; | ||
1388 | struct mei_cl *cl_next = NULL; | ||
1389 | struct list_head *amthi_complete_list = NULL; | ||
1390 | struct mei_cl_cb *cb_pos = NULL; | ||
1391 | struct mei_cl_cb *cb_next = NULL; | ||
1392 | |||
1393 | struct mei_device *dev = container_of(work, | ||
1394 | struct mei_device, wd_work.work); | ||
1395 | |||
1396 | |||
1397 | mutex_lock(&dev->device_lock); | ||
1398 | if (dev->mei_state != MEI_ENABLED) { | ||
1399 | if (dev->mei_state == MEI_INIT_CLIENTS) { | ||
1400 | if (dev->init_clients_timer) { | ||
1401 | if (--dev->init_clients_timer == 0) { | ||
1402 | dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n", | ||
1403 | dev->init_clients_state); | ||
1404 | mei_reset(dev, 1); | ||
1405 | } | ||
1406 | } | ||
1407 | } | ||
1408 | goto out; | ||
1409 | } | ||
1410 | /*** connect/disconnect timeouts ***/ | ||
1411 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | ||
1412 | if (cl_pos->timer_count) { | ||
1413 | if (--cl_pos->timer_count == 0) { | ||
1414 | dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n"); | ||
1415 | mei_reset(dev, 1); | ||
1416 | goto out; | ||
1417 | } | ||
1418 | } | ||
1419 | } | ||
1420 | |||
1421 | if (dev->wd_cl.state != MEI_FILE_CONNECTED) | ||
1422 | goto out; | ||
1423 | |||
1424 | /* Watchdog */ | ||
1425 | if (dev->wd_due_counter && !dev->wd_bypass) { | ||
1426 | if (--dev->wd_due_counter == 0) { | ||
1427 | if (dev->mei_host_buffer_is_empty && | ||
1428 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { | ||
1429 | dev->mei_host_buffer_is_empty = false; | ||
1430 | dev_dbg(&dev->pdev->dev, "send watchdog.\n"); | ||
1431 | |||
1432 | if (mei_wd_send(dev)) | ||
1433 | dev_dbg(&dev->pdev->dev, "wd send failed.\n"); | ||
1434 | else | ||
1435 | if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) | ||
1436 | goto out; | ||
1437 | |||
1438 | if (dev->wd_timeout) | ||
1439 | dev->wd_due_counter = 2; | ||
1440 | else | ||
1441 | dev->wd_due_counter = 0; | ||
1442 | |||
1443 | } else | ||
1444 | dev->wd_pending = true; | ||
1445 | |||
1446 | } | ||
1447 | } | ||
1448 | if (dev->iamthif_stall_timer) { | ||
1449 | if (--dev->iamthif_stall_timer == 0) { | ||
1450 | dev_dbg(&dev->pdev->dev, "reseting because of hang to amthi.\n"); | ||
1451 | mei_reset(dev, 1); | ||
1452 | dev->iamthif_msg_buf_size = 0; | ||
1453 | dev->iamthif_msg_buf_index = 0; | ||
1454 | dev->iamthif_canceled = false; | ||
1455 | dev->iamthif_ioctl = true; | ||
1456 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
1457 | dev->iamthif_timer = 0; | ||
1458 | |||
1459 | if (dev->iamthif_current_cb) | ||
1460 | mei_free_cb_private(dev->iamthif_current_cb); | ||
1461 | |||
1462 | dev->iamthif_file_object = NULL; | ||
1463 | dev->iamthif_current_cb = NULL; | ||
1464 | mei_run_next_iamthif_cmd(dev); | ||
1465 | } | ||
1466 | } | ||
1467 | |||
1468 | if (dev->iamthif_timer) { | ||
1469 | |||
1470 | timeout = dev->iamthif_timer + | ||
1471 | msecs_to_jiffies(IAMTHIF_READ_TIMER); | ||
1472 | |||
1473 | dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", | ||
1474 | dev->iamthif_timer); | ||
1475 | dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); | ||
1476 | dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); | ||
1477 | if (time_after(jiffies, timeout)) { | ||
1478 | /* | ||
1479 | * User didn't read the AMTHI data on time (15sec) | ||
1480 | * freeing AMTHI for other requests | ||
1481 | */ | ||
1482 | |||
1483 | dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); | ||
1484 | |||
1485 | amthi_complete_list = &dev->amthi_read_complete_list. | ||
1486 | mei_cb.cb_list; | ||
1487 | |||
1488 | if (!list_empty(amthi_complete_list)) { | ||
1489 | |||
1490 | list_for_each_entry_safe(cb_pos, cb_next, | ||
1491 | amthi_complete_list, | ||
1492 | cb_list) { | ||
1493 | |||
1494 | cl_pos = cb_pos->file_object->private_data; | ||
1495 | |||
1496 | /* Finding the AMTHI entry. */ | ||
1497 | if (cl_pos == &dev->iamthif_cl) | ||
1498 | list_del(&cb_pos->cb_list); | ||
1499 | } | ||
1500 | } | ||
1501 | if (dev->iamthif_current_cb) | ||
1502 | mei_free_cb_private(dev->iamthif_current_cb); | ||
1503 | |||
1504 | dev->iamthif_file_object->private_data = NULL; | ||
1505 | dev->iamthif_file_object = NULL; | ||
1506 | dev->iamthif_current_cb = NULL; | ||
1507 | dev->iamthif_timer = 0; | ||
1508 | mei_run_next_iamthif_cmd(dev); | ||
1509 | |||
1510 | } | ||
1511 | } | ||
1512 | out: | ||
1513 | schedule_delayed_work(&dev->wd_work, 2 * HZ); | ||
1514 | mutex_unlock(&dev->device_lock); | ||
1515 | } | ||
1516 | |||
1517 | /** | ||
1518 | * mei_interrupt_thread_handler - function called after ISR to handle the interrupt | ||
1519 | * processing. | ||
1520 | * | ||
1521 | * @irq: The irq number | ||
1522 | * @dev_id: pointer to the device structure | ||
1523 | * | ||
1524 | * returns irqreturn_t | ||
1525 | * | ||
1526 | */ | ||
1527 | irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) | ||
1528 | { | ||
1529 | struct mei_device *dev = (struct mei_device *) dev_id; | ||
1530 | struct mei_io_list complete_list; | ||
1531 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | ||
1532 | struct mei_cl *cl; | ||
1533 | s32 slots; | ||
1534 | int rets; | ||
1535 | bool bus_message_received; | ||
1536 | |||
1537 | |||
1538 | dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); | ||
1539 | /* initialize our complete list */ | ||
1540 | mutex_lock(&dev->device_lock); | ||
1541 | mei_io_list_init(&complete_list); | ||
1542 | dev->host_hw_state = mei_hcsr_read(dev); | ||
1543 | dev->me_hw_state = mei_mecsr_read(dev); | ||
1544 | |||
1545 | /* check if ME wants a reset */ | ||
1546 | if ((dev->me_hw_state & ME_RDY_HRA) == 0 && | ||
1547 | dev->mei_state != MEI_RESETING && | ||
1548 | dev->mei_state != MEI_INITIALIZING) { | ||
1549 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); | ||
1550 | mei_reset(dev, 1); | ||
1551 | mutex_unlock(&dev->device_lock); | ||
1552 | return IRQ_HANDLED; | ||
1553 | } | ||
1554 | |||
1555 | /* check if we need to start the dev */ | ||
1556 | if ((dev->host_hw_state & H_RDY) == 0) { | ||
1557 | if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) { | ||
1558 | dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); | ||
1559 | dev->host_hw_state |= (H_IE | H_IG | H_RDY); | ||
1560 | mei_hcsr_set(dev); | ||
1561 | dev->mei_state = MEI_INIT_CLIENTS; | ||
1562 | dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); | ||
1563 | /* link is established | ||
1564 | * start sending messages. | ||
1565 | */ | ||
1566 | mei_host_start_message(dev); | ||
1567 | mutex_unlock(&dev->device_lock); | ||
1568 | return IRQ_HANDLED; | ||
1569 | } else { | ||
1570 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); | ||
1571 | mutex_unlock(&dev->device_lock); | ||
1572 | return IRQ_HANDLED; | ||
1573 | } | ||
1574 | } | ||
1575 | /* check slots avalable for reading */ | ||
1576 | slots = mei_count_full_read_slots(dev); | ||
1577 | dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n", | ||
1578 | slots, dev->extra_write_index); | ||
1579 | while (slots > 0 && !dev->extra_write_index) { | ||
1580 | dev_dbg(&dev->pdev->dev, "slots =%08x extra_write_index =%08x.\n", | ||
1581 | slots, dev->extra_write_index); | ||
1582 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n"); | ||
1583 | rets = mei_irq_thread_read_handler(&complete_list, dev, &slots); | ||
1584 | if (rets) | ||
1585 | goto end; | ||
1586 | } | ||
1587 | rets = mei_irq_thread_write_handler(&complete_list, dev, &slots); | ||
1588 | end: | ||
1589 | dev_dbg(&dev->pdev->dev, "end of bottom half function.\n"); | ||
1590 | dev->host_hw_state = mei_hcsr_read(dev); | ||
1591 | dev->mei_host_buffer_is_empty = mei_host_buffer_is_empty(dev); | ||
1592 | |||
1593 | bus_message_received = false; | ||
1594 | if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { | ||
1595 | dev_dbg(&dev->pdev->dev, "received waiting bus message\n"); | ||
1596 | bus_message_received = true; | ||
1597 | } | ||
1598 | mutex_unlock(&dev->device_lock); | ||
1599 | if (bus_message_received) { | ||
1600 | dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n"); | ||
1601 | wake_up_interruptible(&dev->wait_recvd_msg); | ||
1602 | bus_message_received = false; | ||
1603 | } | ||
1604 | if (complete_list.status || list_empty(&complete_list.mei_cb.cb_list)) | ||
1605 | return IRQ_HANDLED; | ||
1606 | |||
1607 | |||
1608 | list_for_each_entry_safe(cb_pos, cb_next, | ||
1609 | &complete_list.mei_cb.cb_list, cb_list) { | ||
1610 | cl = (struct mei_cl *)cb_pos->file_private; | ||
1611 | list_del(&cb_pos->cb_list); | ||
1612 | if (cl) { | ||
1613 | if (cl != &dev->iamthif_cl) { | ||
1614 | dev_dbg(&dev->pdev->dev, "completing call back.\n"); | ||
1615 | _mei_cmpl(cl, cb_pos); | ||
1616 | cb_pos = NULL; | ||
1617 | } else if (cl == &dev->iamthif_cl) { | ||
1618 | _mei_cmpl_iamthif(dev, cb_pos); | ||
1619 | } | ||
1620 | } | ||
1621 | } | ||
1622 | return IRQ_HANDLED; | ||
1623 | } | ||