]>
Commit | Line | Data |
---|---|---|
fb7d879f OW |
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 { | |
c95efb74 | 97 | mei_run_next_iamthif_cmd(dev); |
fb7d879f OW |
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; | |
479bc59d | 198 | unsigned char *buffer = NULL; |
fb7d879f OW |
199 | |
200 | dev_dbg(&dev->pdev->dev, "start client msg\n"); | |
c8372094 | 201 | if (list_empty(&dev->read_list.mei_cb.cb_list)) |
fb7d879f OW |
202 | 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); | |
fb7d879f OW |
212 | |
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 | ||
247 | quit: | |
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 | */ | |
267 | static 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; | |
eb9af0ac | 279 | dev->iamthif_flow_control_pending = false; |
fb7d879f OW |
280 | 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 | */ | |
303 | static 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 | */ | |
343 | static 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 | */ | |
370 | static 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"); | |
9ce178e5 OW |
395 | |
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 | ||
c95efb74 | 407 | mei_host_init_iamthif(dev); |
fb7d879f OW |
408 | return; |
409 | } | |
410 | ||
411 | if (is_treat_specially_client(&(dev->iamthif_cl), rs)) { | |
412 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
413 | return; | |
414 | } | |
b7cd2d9f TW |
415 | 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)) { | |
fb7d879f | 425 | list_del(&cb_pos->cb_list); |
b7cd2d9f TW |
426 | cl->status = 0; |
427 | cl->timer_count = 0; | |
428 | break; | |
fb7d879f OW |
429 | } |
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 | */ | |
440 | static 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 | ||
b7cd2d9f TW |
455 | 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; | |
fb7d879f | 458 | |
b7cd2d9f TW |
459 | if (!cl) { |
460 | list_del(&cb_pos->cb_list); | |
461 | return; | |
462 | } | |
fb7d879f | 463 | |
b7cd2d9f TW |
464 | 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) { | |
fb7d879f | 467 | |
b7cd2d9f TW |
468 | list_del(&cb_pos->cb_list); |
469 | if (!rs->status) | |
470 | cl->state = MEI_FILE_DISCONNECTED; | |
fb7d879f | 471 | |
b7cd2d9f TW |
472 | cl->status = 0; |
473 | cl->timer_count = 0; | |
474 | break; | |
fb7d879f OW |
475 | } |
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 | */ | |
487 | static 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 | */ | |
499 | static 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 | ||
cf9673da | 505 | for (i = 0; i < dev->me_clients_num; i++) { |
fb7d879f OW |
506 | 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 | */ | |
527 | static 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 | */ | |
569 | static 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 | */ | |
582 | static 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; | |
eb9af0ac | 599 | dev->wd_pending = false; |
fb7d879f OW |
600 | } 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 | */ | |
635 | static 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; | |
abc51b6d | 647 | int res; |
fb7d879f OW |
648 | |
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; | |
c95efb74 | 666 | mei_host_enum_clients_message(dev); |
fb7d879f | 667 | } else { |
eb9af0ac | 668 | dev->recvd_msg = false; |
fb7d879f OW |
669 | 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 | ||
eb9af0ac | 697 | dev->recvd_msg = true; |
fb7d879f OW |
698 | 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; | |
712 | mei_client_disconnect_response(dev, disconnect_res); | |
713 | 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 | } | |
730 | if (dev->me_clients[dev->me_client_presentation_num] | |
731 | .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++; | |
abc51b6d OW |
741 | |
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 | ||
fb7d879f OW |
773 | } 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; | |
c95efb74 | 793 | mei_allocate_me_clients_storage(dev); |
fb7d879f OW |
794 | dev->init_clients_state = |
795 | MEI_CLIENT_PROPERTIES_MESSAGE; | |
c95efb74 | 796 | mei_host_client_properties(dev); |
fb7d879f OW |
797 | } 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 | */ | |
854 | static 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 | */ | |
894 | static 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 | */ | |
934 | static 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 | */ | |
1030 | static 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; | |
eb9af0ac | 1066 | dev->iamthif_flow_control_pending = true; |
fb7d879f OW |
1067 | /* 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 | */ | |
1113 | static 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 | } | |
1201 | end: | |
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 | */ | |
1216 | static 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; | |
b7cd2d9f | 1222 | struct mei_cl_cb *pos = NULL, *next = NULL; |
fb7d879f OW |
1223 | 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 | } | |
fb7d879f OW |
1230 | *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; | |
b7cd2d9f TW |
1235 | 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; | |
1240 | ||
1241 | 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; | |
fb7d879f | 1259 | } |
fb7d879f OW |
1260 | } |
1261 | } | |
1262 | ||
1263 | if (dev->stop && !dev->wd_pending) { | |
eb9af0ac | 1264 | dev->wd_stopped = true; |
fb7d879f OW |
1265 | 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 | ||
eb9af0ac | 1288 | dev->wd_pending = false; |
fb7d879f OW |
1289 | |
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 */ | |
c8372094 | 1306 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); |
b7cd2d9f | 1307 | list_for_each_entry_safe(pos, next, |
fb7d879f | 1308 | &dev->ctrl_wr_list.mei_cb.cb_list, cb_list) { |
b7cd2d9f | 1309 | cl = (struct mei_cl *) pos->file_private; |
c8372094 | 1310 | if (!cl) { |
b7cd2d9f | 1311 | list_del(&pos->cb_list); |
c8372094 TW |
1312 | return -ENODEV; |
1313 | } | |
b7cd2d9f | 1314 | switch (pos->major_file_operations) { |
c8372094 TW |
1315 | case MEI_CLOSE: |
1316 | /* send disconnect message */ | |
b7cd2d9f | 1317 | ret = _mei_irq_thread_close(dev, slots, pos, cl, cmpl_list); |
c8372094 TW |
1318 | if (ret) |
1319 | return ret; | |
fb7d879f | 1320 | |
c8372094 TW |
1321 | break; |
1322 | case MEI_READ: | |
1323 | /* send flow control message */ | |
b7cd2d9f | 1324 | ret = _mei_irq_thread_read(dev, slots, pos, cl, cmpl_list); |
c8372094 TW |
1325 | if (ret) |
1326 | return ret; | |
fb7d879f | 1327 | |
c8372094 TW |
1328 | break; |
1329 | case MEI_IOCTL: | |
1330 | /* connect message */ | |
e8cd29d8 | 1331 | if (mei_other_client_is_connecting(dev, cl)) |
c8372094 | 1332 | continue; |
b7cd2d9f | 1333 | ret = _mei_irq_thread_ioctl(dev, slots, pos, cl, cmpl_list); |
c8372094 TW |
1334 | if (ret) |
1335 | return ret; | |
fb7d879f | 1336 | |
c8372094 | 1337 | break; |
fb7d879f | 1338 | |
c8372094 TW |
1339 | default: |
1340 | BUG(); | |
fb7d879f | 1341 | } |
c8372094 | 1342 | |
fb7d879f OW |
1343 | } |
1344 | /* complete write list CB */ | |
b7cd2d9f TW |
1345 | 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; | |
1351 | ||
1352 | 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; | |
1360 | } | |
1361 | ret = _mei_irq_thread_cmpl(dev, slots, | |
1362 | pos, | |
1363 | cl, cmpl_list); | |
1364 | if (ret) | |
1365 | return ret; | |
fb7d879f | 1366 | |
b7cd2d9f TW |
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; | |
fb7d879f | 1377 | } |
b7cd2d9f TW |
1378 | ret = _mei_irq_thread_cmpl_iamthif(dev, |
1379 | slots, | |
1380 | pos, | |
1381 | cl, | |
1382 | cmpl_list); | |
1383 | if (ret) | |
1384 | return ret; | |
fb7d879f OW |
1385 | |
1386 | } | |
b7cd2d9f | 1387 | |
fb7d879f OW |
1388 | } |
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 | */ | |
a61c6530 | 1401 | void mei_timer(struct work_struct *work) |
fb7d879f OW |
1402 | { |
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, | |
a61c6530 | 1411 | struct mei_device, timer_work.work); |
fb7d879f OW |
1412 | |
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 | ||
fb7d879f OW |
1438 | 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; | |
eb9af0ac TW |
1444 | dev->iamthif_canceled = false; |
1445 | dev->iamthif_ioctl = true; | |
fb7d879f OW |
1446 | 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; | |
c95efb74 | 1454 | mei_run_next_iamthif_cmd(dev); |
fb7d879f OW |
1455 | } |
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 | ||
b7cd2d9f | 1478 | list_for_each_entry_safe(cb_pos, cb_next, amthi_complete_list, cb_list) { |
fb7d879f | 1479 | |
b7cd2d9f | 1480 | cl_pos = cb_pos->file_object->private_data; |
fb7d879f | 1481 | |
b7cd2d9f TW |
1482 | /* Finding the AMTHI entry. */ |
1483 | if (cl_pos == &dev->iamthif_cl) | |
1484 | list_del(&cb_pos->cb_list); | |
fb7d879f OW |
1485 | } |
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; | |
c95efb74 | 1493 | mei_run_next_iamthif_cmd(dev); |
fb7d879f OW |
1494 | |
1495 | } | |
1496 | } | |
1497 | out: | |
a61c6530 | 1498 | schedule_delayed_work(&dev->timer_work, 2 * HZ); |
fb7d879f OW |
1499 | mutex_unlock(&dev->device_lock); |
1500 | } | |
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 | */ | |
1512 | irqreturn_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); | |
0288c7c9 | 1526 | mei_io_list_init(&complete_list); |
fb7d879f | 1527 | dev->host_hw_state = mei_hcsr_read(dev); |
4f61a7ad TW |
1528 | |
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 | ||
fb7d879f OW |
1534 | 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 | */ | |
c95efb74 | 1557 | mei_host_start_message(dev); |
fb7d879f OW |
1558 | 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); | |
1579 | end: | |
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 | } | |
c8372094 | 1595 | if (list_empty(&complete_list.mei_cb.cb_list)) |
fb7d879f OW |
1596 | 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 | } |