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