]>
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 | { | |
4b8960b4 | 60 | if (cb_pos->fop_type == MEI_FOP_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 | ||
4b8960b4 | 67 | } else if (cb_pos->fop_type == MEI_FOP_READ && |
fb7d879f OW |
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 | ||
fb7d879f OW |
76 | /** |
77 | * _mei_irq_thread_state_ok - checks if mei header matches file private data | |
78 | * | |
79 | * @cl: private data of the file object | |
80 | * @mei_hdr: header of mei client message | |
81 | * | |
82 | * returns !=0 if matches, 0 if no match. | |
83 | */ | |
84 | static int _mei_irq_thread_state_ok(struct mei_cl *cl, | |
85 | struct mei_msg_hdr *mei_hdr) | |
86 | { | |
87 | return (cl->host_client_id == mei_hdr->host_addr && | |
88 | cl->me_client_id == mei_hdr->me_addr && | |
89 | cl->state == MEI_FILE_CONNECTED && | |
90 | MEI_READ_COMPLETE != cl->reading_state); | |
91 | } | |
92 | ||
93 | /** | |
94 | * mei_irq_thread_read_client_message - bottom half read routine after ISR to | |
95 | * handle the read mei client message data processing. | |
96 | * | |
97 | * @complete_list: An instance of our list structure | |
98 | * @dev: the device structure | |
99 | * @mei_hdr: header of mei client message | |
100 | * | |
101 | * returns 0 on success, <0 on failure. | |
102 | */ | |
fb601adb | 103 | static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list, |
fb7d879f OW |
104 | struct mei_device *dev, |
105 | struct mei_msg_hdr *mei_hdr) | |
106 | { | |
107 | struct mei_cl *cl; | |
108 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | |
479bc59d | 109 | unsigned char *buffer = NULL; |
fb7d879f OW |
110 | |
111 | dev_dbg(&dev->pdev->dev, "start client msg\n"); | |
fb601adb | 112 | if (list_empty(&dev->read_list.list)) |
fb7d879f OW |
113 | goto quit; |
114 | ||
fb601adb | 115 | list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) { |
db3ed431 | 116 | cl = cb_pos->cl; |
fb7d879f OW |
117 | if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { |
118 | cl->reading_state = MEI_READING; | |
ebb108ef | 119 | buffer = cb_pos->response_buffer.data + cb_pos->buf_idx; |
fb7d879f OW |
120 | |
121 | if (cb_pos->response_buffer.size < | |
ebb108ef | 122 | mei_hdr->length + cb_pos->buf_idx) { |
fb7d879f | 123 | dev_dbg(&dev->pdev->dev, "message overflow.\n"); |
fb601adb | 124 | list_del(&cb_pos->list); |
fb7d879f OW |
125 | return -ENOMEM; |
126 | } | |
127 | if (buffer) | |
128 | mei_read_slots(dev, buffer, mei_hdr->length); | |
129 | ||
ebb108ef | 130 | cb_pos->buf_idx += mei_hdr->length; |
fb7d879f OW |
131 | if (mei_hdr->msg_complete) { |
132 | cl->status = 0; | |
fb601adb | 133 | list_del(&cb_pos->list); |
fb7d879f | 134 | dev_dbg(&dev->pdev->dev, |
a4136b49 | 135 | "completed read H cl = %d, ME cl = %d, length = %lu\n", |
fb7d879f OW |
136 | cl->host_client_id, |
137 | cl->me_client_id, | |
ebb108ef TW |
138 | cb_pos->buf_idx); |
139 | ||
fb601adb TW |
140 | list_add_tail(&cb_pos->list, |
141 | &complete_list->list); | |
fb7d879f OW |
142 | } |
143 | ||
144 | break; | |
145 | } | |
146 | ||
147 | } | |
148 | ||
149 | quit: | |
150 | dev_dbg(&dev->pdev->dev, "message read\n"); | |
151 | if (!buffer) { | |
edf1eed4 | 152 | mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); |
15d4acc5 TW |
153 | dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n", |
154 | MEI_HDR_PRM(mei_hdr)); | |
fb7d879f OW |
155 | } |
156 | ||
157 | return 0; | |
158 | } | |
159 | ||
fb7d879f OW |
160 | /** |
161 | * _mei_irq_thread_close - processes close related operation. | |
162 | * | |
163 | * @dev: the device structure. | |
164 | * @slots: free slots. | |
165 | * @cb_pos: callback block. | |
166 | * @cl: private data of the file object. | |
167 | * @cmpl_list: complete list. | |
168 | * | |
169 | * returns 0, OK; otherwise, error. | |
170 | */ | |
171 | static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, | |
172 | struct mei_cl_cb *cb_pos, | |
173 | struct mei_cl *cl, | |
fb601adb | 174 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 175 | { |
b45f3ccf | 176 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
aeba4a06 | 177 | sizeof(struct hbm_client_connect_request))) |
b45f3ccf | 178 | return -EBADMSG; |
fb7d879f | 179 | |
aeba4a06 | 180 | *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); |
b45f3ccf TW |
181 | |
182 | if (mei_disconnect(dev, cl)) { | |
183 | cl->status = 0; | |
ebb108ef | 184 | cb_pos->buf_idx = 0; |
fb601adb | 185 | list_move_tail(&cb_pos->list, &cmpl_list->list); |
b45f3ccf | 186 | return -EMSGSIZE; |
fb7d879f | 187 | } else { |
b45f3ccf TW |
188 | cl->state = MEI_FILE_DISCONNECTING; |
189 | cl->status = 0; | |
ebb108ef | 190 | cb_pos->buf_idx = 0; |
fb601adb | 191 | list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); |
b45f3ccf | 192 | cl->timer_count = MEI_CONNECT_TIMEOUT; |
fb7d879f OW |
193 | } |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | /** | |
199 | * is_treat_specially_client - checks if the message belongs | |
200 | * to the file private data. | |
201 | * | |
202 | * @cl: private data of the file object | |
203 | * @rs: connect response bus message | |
204 | * | |
205 | */ | |
206 | static bool is_treat_specially_client(struct mei_cl *cl, | |
207 | struct hbm_client_connect_response *rs) | |
208 | { | |
209 | ||
210 | if (cl->host_client_id == rs->host_addr && | |
211 | cl->me_client_id == rs->me_addr) { | |
212 | if (!rs->status) { | |
213 | cl->state = MEI_FILE_CONNECTED; | |
214 | cl->status = 0; | |
215 | ||
216 | } else { | |
217 | cl->state = MEI_FILE_DISCONNECTED; | |
218 | cl->status = -ENODEV; | |
219 | } | |
220 | cl->timer_count = 0; | |
221 | ||
222 | return true; | |
223 | } | |
224 | return false; | |
225 | } | |
226 | ||
227 | /** | |
228 | * mei_client_connect_response - connects to response irq routine | |
229 | * | |
230 | * @dev: the device structure | |
231 | * @rs: connect response bus message | |
232 | */ | |
233 | static void mei_client_connect_response(struct mei_device *dev, | |
234 | struct hbm_client_connect_response *rs) | |
235 | { | |
236 | ||
237 | struct mei_cl *cl; | |
fb601adb | 238 | struct mei_cl_cb *pos = NULL, *next = NULL; |
fb7d879f OW |
239 | |
240 | dev_dbg(&dev->pdev->dev, | |
241 | "connect_response:\n" | |
242 | "ME Client = %d\n" | |
243 | "Host Client = %d\n" | |
244 | "Status = %d\n", | |
245 | rs->me_addr, | |
246 | rs->host_addr, | |
247 | rs->status); | |
248 | ||
249 | /* if WD or iamthif client treat specially */ | |
250 | ||
251 | if (is_treat_specially_client(&(dev->wd_cl), rs)) { | |
fb7d879f | 252 | dev_dbg(&dev->pdev->dev, "successfully connected to WD client.\n"); |
70cd5337 | 253 | mei_watchdog_register(dev); |
9ce178e5 | 254 | |
fb7d879f OW |
255 | return; |
256 | } | |
257 | ||
258 | if (is_treat_specially_client(&(dev->iamthif_cl), rs)) { | |
259 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
260 | return; | |
261 | } | |
fb601adb | 262 | list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) { |
b7cd2d9f | 263 | |
db3ed431 | 264 | cl = pos->cl; |
b7cd2d9f | 265 | if (!cl) { |
fb601adb | 266 | list_del(&pos->list); |
b7cd2d9f TW |
267 | return; |
268 | } | |
4b8960b4 | 269 | if (pos->fop_type == MEI_FOP_IOCTL) { |
b7cd2d9f | 270 | if (is_treat_specially_client(cl, rs)) { |
fb601adb | 271 | list_del(&pos->list); |
b7cd2d9f TW |
272 | cl->status = 0; |
273 | cl->timer_count = 0; | |
274 | break; | |
fb7d879f OW |
275 | } |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | /** | |
281 | * mei_client_disconnect_response - disconnects from response irq routine | |
282 | * | |
283 | * @dev: the device structure | |
284 | * @rs: disconnect response bus message | |
285 | */ | |
286 | static void mei_client_disconnect_response(struct mei_device *dev, | |
287 | struct hbm_client_connect_response *rs) | |
288 | { | |
289 | struct mei_cl *cl; | |
fb601adb | 290 | struct mei_cl_cb *pos = NULL, *next = NULL; |
fb7d879f OW |
291 | |
292 | dev_dbg(&dev->pdev->dev, | |
293 | "disconnect_response:\n" | |
294 | "ME Client = %d\n" | |
295 | "Host Client = %d\n" | |
296 | "Status = %d\n", | |
297 | rs->me_addr, | |
298 | rs->host_addr, | |
299 | rs->status); | |
300 | ||
fb601adb | 301 | list_for_each_entry_safe(pos, next, &dev->ctrl_rd_list.list, list) { |
db3ed431 | 302 | cl = pos->cl; |
fb7d879f | 303 | |
b7cd2d9f | 304 | if (!cl) { |
fb601adb | 305 | list_del(&pos->list); |
b7cd2d9f TW |
306 | return; |
307 | } | |
fb7d879f | 308 | |
b7cd2d9f TW |
309 | dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in ctrl_rd_list.\n"); |
310 | if (cl->host_client_id == rs->host_addr && | |
311 | cl->me_client_id == rs->me_addr) { | |
fb7d879f | 312 | |
fb601adb | 313 | list_del(&pos->list); |
b7cd2d9f TW |
314 | if (!rs->status) |
315 | cl->state = MEI_FILE_DISCONNECTED; | |
fb7d879f | 316 | |
b7cd2d9f TW |
317 | cl->status = 0; |
318 | cl->timer_count = 0; | |
319 | break; | |
fb7d879f OW |
320 | } |
321 | } | |
322 | } | |
323 | ||
324 | /** | |
325 | * same_flow_addr - tells if they have the same address. | |
326 | * | |
327 | * @file: private data of the file object. | |
328 | * @flow: flow control. | |
329 | * | |
330 | * returns !=0, same; 0,not. | |
331 | */ | |
332 | static int same_flow_addr(struct mei_cl *cl, struct hbm_flow_control *flow) | |
333 | { | |
334 | return (cl->host_client_id == flow->host_addr && | |
335 | cl->me_client_id == flow->me_addr); | |
336 | } | |
337 | ||
338 | /** | |
339 | * add_single_flow_creds - adds single buffer credentials. | |
340 | * | |
341 | * @file: private data ot the file object. | |
342 | * @flow: flow control. | |
343 | */ | |
344 | static void add_single_flow_creds(struct mei_device *dev, | |
345 | struct hbm_flow_control *flow) | |
346 | { | |
347 | struct mei_me_client *client; | |
348 | int i; | |
349 | ||
cf9673da | 350 | for (i = 0; i < dev->me_clients_num; i++) { |
fb7d879f OW |
351 | client = &dev->me_clients[i]; |
352 | if (client && flow->me_addr == client->client_id) { | |
353 | if (client->props.single_recv_buf) { | |
354 | client->mei_flow_ctrl_creds++; | |
355 | dev_dbg(&dev->pdev->dev, "recv flow ctrl msg ME %d (single).\n", | |
356 | flow->me_addr); | |
357 | dev_dbg(&dev->pdev->dev, "flow control credentials =%d.\n", | |
358 | client->mei_flow_ctrl_creds); | |
359 | } else { | |
360 | BUG(); /* error in flow control */ | |
361 | } | |
362 | } | |
363 | } | |
364 | } | |
365 | ||
366 | /** | |
367 | * mei_client_flow_control_response - flow control response irq routine | |
368 | * | |
369 | * @dev: the device structure | |
370 | * @flow_control: flow control response bus message | |
371 | */ | |
372 | static void mei_client_flow_control_response(struct mei_device *dev, | |
373 | struct hbm_flow_control *flow_control) | |
374 | { | |
375 | struct mei_cl *cl_pos = NULL; | |
376 | struct mei_cl *cl_next = NULL; | |
377 | ||
378 | if (!flow_control->host_addr) { | |
379 | /* single receive buffer */ | |
380 | add_single_flow_creds(dev, flow_control); | |
381 | } else { | |
382 | /* normal connection */ | |
383 | list_for_each_entry_safe(cl_pos, cl_next, | |
384 | &dev->file_list, link) { | |
385 | dev_dbg(&dev->pdev->dev, "list_for_each_entry_safe in file_list\n"); | |
386 | ||
387 | dev_dbg(&dev->pdev->dev, "cl of host client %d ME client %d.\n", | |
388 | cl_pos->host_client_id, | |
389 | cl_pos->me_client_id); | |
390 | dev_dbg(&dev->pdev->dev, "flow ctrl msg for host %d ME %d.\n", | |
391 | flow_control->host_addr, | |
392 | flow_control->me_addr); | |
393 | if (same_flow_addr(cl_pos, flow_control)) { | |
394 | dev_dbg(&dev->pdev->dev, "recv ctrl msg for host %d ME %d.\n", | |
395 | flow_control->host_addr, | |
396 | flow_control->me_addr); | |
397 | cl_pos->mei_flow_ctrl_creds++; | |
398 | dev_dbg(&dev->pdev->dev, "flow control credentials = %d.\n", | |
399 | cl_pos->mei_flow_ctrl_creds); | |
400 | break; | |
401 | } | |
402 | } | |
403 | } | |
404 | } | |
405 | ||
406 | /** | |
407 | * same_disconn_addr - tells if they have the same address | |
408 | * | |
409 | * @file: private data of the file object. | |
410 | * @disconn: disconnection request. | |
411 | * | |
412 | * returns !=0, same; 0,not. | |
413 | */ | |
414 | static int same_disconn_addr(struct mei_cl *cl, | |
aeba4a06 | 415 | struct hbm_client_connect_request *req) |
fb7d879f | 416 | { |
aeba4a06 TW |
417 | return (cl->host_client_id == req->host_addr && |
418 | cl->me_client_id == req->me_addr); | |
fb7d879f OW |
419 | } |
420 | ||
421 | /** | |
422 | * mei_client_disconnect_request - disconnects from request irq routine | |
423 | * | |
424 | * @dev: the device structure. | |
425 | * @disconnect_req: disconnect request bus message. | |
426 | */ | |
427 | static void mei_client_disconnect_request(struct mei_device *dev, | |
aeba4a06 | 428 | struct hbm_client_connect_request *disconnect_req) |
fb7d879f | 429 | { |
fb7d879f | 430 | struct hbm_client_connect_response *disconnect_res; |
5bd64714 TW |
431 | struct mei_cl *pos, *next; |
432 | const size_t len = sizeof(struct hbm_client_connect_response); | |
fb7d879f | 433 | |
5bd64714 TW |
434 | list_for_each_entry_safe(pos, next, &dev->file_list, link) { |
435 | if (same_disconn_addr(pos, disconnect_req)) { | |
fb7d879f OW |
436 | dev_dbg(&dev->pdev->dev, "disconnect request host client %d ME client %d.\n", |
437 | disconnect_req->host_addr, | |
438 | disconnect_req->me_addr); | |
5bd64714 TW |
439 | pos->state = MEI_FILE_DISCONNECTED; |
440 | pos->timer_count = 0; | |
441 | if (pos == &dev->wd_cl) | |
eb9af0ac | 442 | dev->wd_pending = false; |
5bd64714 | 443 | else if (pos == &dev->iamthif_cl) |
fb7d879f OW |
444 | dev->iamthif_timer = 0; |
445 | ||
446 | /* prepare disconnect response */ | |
5fb54fb4 | 447 | (void)mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len); |
fb7d879f OW |
448 | disconnect_res = |
449 | (struct hbm_client_connect_response *) | |
5fb54fb4 | 450 | &dev->wr_ext_msg.data; |
1ca7e782 | 451 | disconnect_res->hbm_cmd = CLIENT_DISCONNECT_RES_CMD; |
5bd64714 TW |
452 | disconnect_res->host_addr = pos->host_client_id; |
453 | disconnect_res->me_addr = pos->me_client_id; | |
fb7d879f | 454 | disconnect_res->status = 0; |
fb7d879f OW |
455 | break; |
456 | } | |
457 | } | |
458 | } | |
459 | ||
fb7d879f OW |
460 | /** |
461 | * mei_irq_thread_read_bus_message - bottom half read routine after ISR to | |
462 | * handle the read bus message cmd processing. | |
463 | * | |
464 | * @dev: the device structure | |
465 | * @mei_hdr: header of bus message | |
466 | */ | |
467 | static void mei_irq_thread_read_bus_message(struct mei_device *dev, | |
438763f3 | 468 | struct mei_msg_hdr *hdr) |
fb7d879f OW |
469 | { |
470 | struct mei_bus_message *mei_msg; | |
c1174c0e | 471 | struct mei_me_client *me_client; |
fb7d879f OW |
472 | struct hbm_host_version_response *version_res; |
473 | struct hbm_client_connect_response *connect_res; | |
474 | struct hbm_client_connect_response *disconnect_res; | |
aeba4a06 | 475 | struct hbm_client_connect_request *disconnect_req; |
fb7d879f OW |
476 | struct hbm_flow_control *flow_control; |
477 | struct hbm_props_response *props_res; | |
478 | struct hbm_host_enum_response *enum_res; | |
5bd64714 | 479 | struct hbm_host_stop_request *stop_req; |
fb7d879f OW |
480 | |
481 | /* read the message to our buffer */ | |
438763f3 TW |
482 | BUG_ON(hdr->length >= sizeof(dev->rd_msg_buf)); |
483 | mei_read_slots(dev, dev->rd_msg_buf, hdr->length); | |
edf1eed4 | 484 | mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; |
fb7d879f | 485 | |
1ca7e782 | 486 | switch (mei_msg->hbm_cmd) { |
fb7d879f OW |
487 | case HOST_START_RES_CMD: |
488 | version_res = (struct hbm_host_version_response *) mei_msg; | |
489 | if (version_res->host_version_supported) { | |
490 | dev->version.major_version = HBM_MAJOR_VERSION; | |
491 | dev->version.minor_version = HBM_MINOR_VERSION; | |
b210d750 | 492 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && |
fb7d879f OW |
493 | dev->init_clients_state == MEI_START_MESSAGE) { |
494 | dev->init_clients_timer = 0; | |
c95efb74 | 495 | mei_host_enum_clients_message(dev); |
fb7d879f | 496 | } else { |
eb9af0ac | 497 | dev->recvd_msg = false; |
fb7d879f OW |
498 | dev_dbg(&dev->pdev->dev, "IMEI reset due to received host start response bus message.\n"); |
499 | mei_reset(dev, 1); | |
500 | return; | |
501 | } | |
502 | } else { | |
5bd64714 TW |
503 | u32 *buf = dev->wr_msg_buf; |
504 | const size_t len = sizeof(struct hbm_host_stop_request); | |
505 | ||
fb7d879f | 506 | dev->version = version_res->me_max_version; |
5bd64714 | 507 | |
fb7d879f | 508 | /* send stop message */ |
438763f3 | 509 | hdr = mei_hbm_hdr(&buf[0], len); |
5bd64714 TW |
510 | stop_req = (struct hbm_host_stop_request *)&buf[1]; |
511 | memset(stop_req, 0, len); | |
512 | stop_req->hbm_cmd = HOST_STOP_REQ_CMD; | |
513 | stop_req->reason = DRIVER_STOP_REQUEST; | |
514 | ||
438763f3 | 515 | mei_write_message(dev, hdr, (unsigned char *)stop_req); |
fb7d879f OW |
516 | dev_dbg(&dev->pdev->dev, "version mismatch.\n"); |
517 | return; | |
518 | } | |
519 | ||
eb9af0ac | 520 | dev->recvd_msg = true; |
fb7d879f OW |
521 | dev_dbg(&dev->pdev->dev, "host start response message received.\n"); |
522 | break; | |
523 | ||
524 | case CLIENT_CONNECT_RES_CMD: | |
5bd64714 | 525 | connect_res = (struct hbm_client_connect_response *) mei_msg; |
fb7d879f OW |
526 | mei_client_connect_response(dev, connect_res); |
527 | dev_dbg(&dev->pdev->dev, "client connect response message received.\n"); | |
528 | wake_up(&dev->wait_recvd_msg); | |
529 | break; | |
530 | ||
531 | case CLIENT_DISCONNECT_RES_CMD: | |
5bd64714 | 532 | disconnect_res = (struct hbm_client_connect_response *) mei_msg; |
441ab50f | 533 | mei_client_disconnect_response(dev, disconnect_res); |
fb7d879f OW |
534 | dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n"); |
535 | wake_up(&dev->wait_recvd_msg); | |
536 | break; | |
537 | ||
538 | case MEI_FLOW_CONTROL_CMD: | |
539 | flow_control = (struct hbm_flow_control *) mei_msg; | |
540 | mei_client_flow_control_response(dev, flow_control); | |
541 | dev_dbg(&dev->pdev->dev, "client flow control response message received.\n"); | |
542 | break; | |
543 | ||
544 | case HOST_CLIENT_PROPERTIES_RES_CMD: | |
545 | props_res = (struct hbm_props_response *)mei_msg; | |
c1174c0e SO |
546 | me_client = &dev->me_clients[dev->me_client_presentation_num]; |
547 | ||
fb7d879f OW |
548 | if (props_res->status || !dev->me_clients) { |
549 | dev_dbg(&dev->pdev->dev, "reset due to received host client properties response bus message wrong status.\n"); | |
550 | mei_reset(dev, 1); | |
551 | return; | |
552 | } | |
fb7d879f | 553 | |
c1174c0e SO |
554 | if (me_client->client_id != props_res->address) { |
555 | dev_err(&dev->pdev->dev, | |
556 | "Host client properties reply mismatch\n"); | |
557 | mei_reset(dev, 1); | |
fb7d879f | 558 | |
c1174c0e SO |
559 | return; |
560 | } | |
abc51b6d | 561 | |
c1174c0e SO |
562 | if (dev->dev_state != MEI_DEV_INIT_CLIENTS || |
563 | dev->init_clients_state != MEI_CLIENT_PROPERTIES_MESSAGE) { | |
564 | dev_err(&dev->pdev->dev, | |
565 | "Unexpected client properties reply\n"); | |
fb7d879f | 566 | mei_reset(dev, 1); |
c1174c0e | 567 | |
fb7d879f OW |
568 | return; |
569 | } | |
c1174c0e SO |
570 | |
571 | me_client->props = props_res->client_properties; | |
572 | dev->me_client_index++; | |
573 | dev->me_client_presentation_num++; | |
574 | ||
575 | mei_host_client_enumerate(dev); | |
576 | ||
fb7d879f OW |
577 | break; |
578 | ||
579 | case HOST_ENUM_RES_CMD: | |
580 | enum_res = (struct hbm_host_enum_response *) mei_msg; | |
581 | memcpy(dev->me_clients_map, enum_res->valid_addresses, 32); | |
b210d750 | 582 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && |
fb7d879f OW |
583 | dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) { |
584 | dev->init_clients_timer = 0; | |
585 | dev->me_client_presentation_num = 0; | |
586 | dev->me_client_index = 0; | |
c95efb74 | 587 | mei_allocate_me_clients_storage(dev); |
fb7d879f OW |
588 | dev->init_clients_state = |
589 | MEI_CLIENT_PROPERTIES_MESSAGE; | |
c1174c0e SO |
590 | |
591 | mei_host_client_enumerate(dev); | |
fb7d879f OW |
592 | } else { |
593 | dev_dbg(&dev->pdev->dev, "reset due to received host enumeration clients response bus message.\n"); | |
594 | mei_reset(dev, 1); | |
595 | return; | |
596 | } | |
597 | break; | |
598 | ||
599 | case HOST_STOP_RES_CMD: | |
b210d750 | 600 | dev->dev_state = MEI_DEV_DISABLED; |
fb7d879f OW |
601 | dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n"); |
602 | mei_reset(dev, 1); | |
603 | break; | |
604 | ||
605 | case CLIENT_DISCONNECT_REQ_CMD: | |
606 | /* search for client */ | |
aeba4a06 | 607 | disconnect_req = (struct hbm_client_connect_request *)mei_msg; |
fb7d879f OW |
608 | mei_client_disconnect_request(dev, disconnect_req); |
609 | break; | |
610 | ||
611 | case ME_STOP_REQ_CMD: | |
5bd64714 TW |
612 | { |
613 | /* prepare stop request: sent in next interrupt event */ | |
614 | ||
5bd64714 TW |
615 | const size_t len = sizeof(struct hbm_host_stop_request); |
616 | ||
438763f3 | 617 | hdr = mei_hbm_hdr((u32 *)&dev->wr_ext_msg.hdr, len); |
5fb54fb4 | 618 | stop_req = (struct hbm_host_stop_request *)&dev->wr_ext_msg.data; |
5bd64714 TW |
619 | memset(stop_req, 0, len); |
620 | stop_req->hbm_cmd = HOST_STOP_REQ_CMD; | |
621 | stop_req->reason = DRIVER_STOP_REQUEST; | |
fb7d879f | 622 | break; |
5bd64714 | 623 | } |
fb7d879f OW |
624 | default: |
625 | BUG(); | |
626 | break; | |
627 | ||
628 | } | |
629 | } | |
630 | ||
631 | ||
632 | /** | |
633 | * _mei_hb_read - processes read related operation. | |
634 | * | |
635 | * @dev: the device structure. | |
636 | * @slots: free slots. | |
637 | * @cb_pos: callback block. | |
638 | * @cl: private data of the file object. | |
639 | * @cmpl_list: complete list. | |
640 | * | |
641 | * returns 0, OK; otherwise, error. | |
642 | */ | |
643 | static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, | |
644 | struct mei_cl_cb *cb_pos, | |
645 | struct mei_cl *cl, | |
fb601adb | 646 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 647 | { |
1e69d64a | 648 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
fb7d879f | 649 | sizeof(struct hbm_flow_control))) { |
fb7d879f | 650 | /* return the cancel routine */ |
fb601adb | 651 | list_del(&cb_pos->list); |
fb7d879f OW |
652 | return -EBADMSG; |
653 | } | |
654 | ||
7bdf72d3 TW |
655 | *slots -= mei_data2slots(sizeof(struct hbm_flow_control)); |
656 | ||
1ccb7b62 TW |
657 | if (mei_send_flow_control(dev, cl)) { |
658 | cl->status = -ENODEV; | |
ebb108ef | 659 | cb_pos->buf_idx = 0; |
fb601adb | 660 | list_move_tail(&cb_pos->list, &cmpl_list->list); |
1ccb7b62 TW |
661 | return -ENODEV; |
662 | } | |
fb601adb | 663 | list_move_tail(&cb_pos->list, &dev->read_list.list); |
1ccb7b62 | 664 | |
fb7d879f OW |
665 | return 0; |
666 | } | |
667 | ||
668 | ||
669 | /** | |
670 | * _mei_irq_thread_ioctl - processes ioctl related operation. | |
671 | * | |
672 | * @dev: the device structure. | |
673 | * @slots: free slots. | |
674 | * @cb_pos: callback block. | |
675 | * @cl: private data of the file object. | |
676 | * @cmpl_list: complete list. | |
677 | * | |
678 | * returns 0, OK; otherwise, error. | |
679 | */ | |
680 | static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, | |
681 | struct mei_cl_cb *cb_pos, | |
682 | struct mei_cl *cl, | |
fb601adb | 683 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 684 | { |
b45f3ccf | 685 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
fb7d879f | 686 | sizeof(struct hbm_client_connect_request))) { |
fb7d879f | 687 | /* return the cancel routine */ |
fb601adb | 688 | list_del(&cb_pos->list); |
fb7d879f OW |
689 | return -EBADMSG; |
690 | } | |
691 | ||
b45f3ccf TW |
692 | cl->state = MEI_FILE_CONNECTING; |
693 | *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); | |
694 | if (mei_connect(dev, cl)) { | |
695 | cl->status = -ENODEV; | |
ebb108ef | 696 | cb_pos->buf_idx = 0; |
fb601adb | 697 | list_del(&cb_pos->list); |
b45f3ccf TW |
698 | return -ENODEV; |
699 | } else { | |
fb601adb | 700 | list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); |
b45f3ccf TW |
701 | cl->timer_count = MEI_CONNECT_TIMEOUT; |
702 | } | |
fb7d879f OW |
703 | return 0; |
704 | } | |
705 | ||
706 | /** | |
ea3b5fb7 | 707 | * mei_irq_thread_write_complete - write messages to device. |
fb7d879f OW |
708 | * |
709 | * @dev: the device structure. | |
710 | * @slots: free slots. | |
ea3b5fb7 | 711 | * @cb: callback block. |
fb7d879f OW |
712 | * @cmpl_list: complete list. |
713 | * | |
714 | * returns 0, OK; otherwise, error. | |
715 | */ | |
ea3b5fb7 TW |
716 | static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots, |
717 | struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) | |
fb7d879f OW |
718 | { |
719 | struct mei_msg_hdr *mei_hdr; | |
ea3b5fb7 TW |
720 | struct mei_cl *cl = cb->cl; |
721 | size_t len = cb->request_buffer.size - cb->buf_idx; | |
722 | size_t msg_slots = mei_data2slots(len); | |
723 | ||
724 | mei_hdr = (struct mei_msg_hdr *)&dev->wr_msg_buf[0]; | |
725 | mei_hdr->host_addr = cl->host_client_id; | |
726 | mei_hdr->me_addr = cl->me_client_id; | |
727 | mei_hdr->reserved = 0; | |
fb7d879f | 728 | |
ea3b5fb7 TW |
729 | if (*slots >= msg_slots) { |
730 | mei_hdr->length = len; | |
fb7d879f | 731 | mei_hdr->msg_complete = 1; |
ea3b5fb7 | 732 | /* Split the message only if we can write the whole host buffer */ |
24aadc80 | 733 | } else if (*slots == dev->hbuf_depth) { |
ea3b5fb7 TW |
734 | msg_slots = *slots; |
735 | len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | |
736 | mei_hdr->length = len; | |
fb7d879f | 737 | mei_hdr->msg_complete = 0; |
fb7d879f | 738 | } else { |
ea3b5fb7 TW |
739 | /* wait for next time the host buffer is empty */ |
740 | return 0; | |
fb7d879f OW |
741 | } |
742 | ||
ea3b5fb7 TW |
743 | dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", |
744 | cb->request_buffer.size, cb->buf_idx); | |
15d4acc5 | 745 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
ea3b5fb7 TW |
746 | |
747 | *slots -= msg_slots; | |
748 | if (mei_write_message(dev, mei_hdr, | |
438763f3 | 749 | cb->request_buffer.data + cb->buf_idx)) { |
ea3b5fb7 TW |
750 | cl->status = -ENODEV; |
751 | list_move_tail(&cb->list, &cmpl_list->list); | |
752 | return -ENODEV; | |
753 | } | |
754 | ||
755 | if (mei_flow_ctrl_reduce(dev, cl)) | |
756 | return -ENODEV; | |
757 | ||
758 | cl->status = 0; | |
759 | cb->buf_idx += mei_hdr->length; | |
760 | if (mei_hdr->msg_complete) | |
761 | list_move_tail(&cb->list, &dev->write_waiting_list.list); | |
762 | ||
fb7d879f OW |
763 | return 0; |
764 | } | |
765 | ||
fb7d879f OW |
766 | /** |
767 | * mei_irq_thread_read_handler - bottom half read routine after ISR to | |
768 | * handle the read processing. | |
769 | * | |
770 | * @cmpl_list: An instance of our list structure | |
771 | * @dev: the device structure | |
772 | * @slots: slots to read. | |
773 | * | |
774 | * returns 0 on success, <0 on failure. | |
775 | */ | |
fb601adb | 776 | static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list, |
fb7d879f OW |
777 | struct mei_device *dev, |
778 | s32 *slots) | |
779 | { | |
780 | struct mei_msg_hdr *mei_hdr; | |
781 | struct mei_cl *cl_pos = NULL; | |
782 | struct mei_cl *cl_next = NULL; | |
783 | int ret = 0; | |
784 | ||
785 | if (!dev->rd_msg_hdr) { | |
786 | dev->rd_msg_hdr = mei_mecbrw_read(dev); | |
787 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | |
788 | (*slots)--; | |
789 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | |
790 | } | |
791 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | |
15d4acc5 | 792 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
fb7d879f OW |
793 | |
794 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | |
795 | dev_dbg(&dev->pdev->dev, "corrupted message header.\n"); | |
796 | ret = -EBADMSG; | |
797 | goto end; | |
798 | } | |
799 | ||
800 | if (mei_hdr->host_addr || mei_hdr->me_addr) { | |
801 | list_for_each_entry_safe(cl_pos, cl_next, | |
802 | &dev->file_list, link) { | |
803 | dev_dbg(&dev->pdev->dev, | |
804 | "list_for_each_entry_safe read host" | |
805 | " client = %d, ME client = %d\n", | |
806 | cl_pos->host_client_id, | |
807 | cl_pos->me_client_id); | |
808 | if (cl_pos->host_client_id == mei_hdr->host_addr && | |
809 | cl_pos->me_client_id == mei_hdr->me_addr) | |
810 | break; | |
811 | } | |
812 | ||
813 | if (&cl_pos->link == &dev->file_list) { | |
814 | dev_dbg(&dev->pdev->dev, "corrupted message header\n"); | |
815 | ret = -EBADMSG; | |
816 | goto end; | |
817 | } | |
818 | } | |
819 | if (((*slots) * sizeof(u32)) < mei_hdr->length) { | |
820 | dev_dbg(&dev->pdev->dev, | |
821 | "we can't read the message slots =%08x.\n", | |
822 | *slots); | |
823 | /* we can't read the message */ | |
824 | ret = -ERANGE; | |
825 | goto end; | |
826 | } | |
827 | ||
828 | /* decide where to read the message too */ | |
829 | if (!mei_hdr->host_addr) { | |
830 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); | |
831 | mei_irq_thread_read_bus_message(dev, mei_hdr); | |
832 | dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); | |
833 | } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && | |
834 | (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && | |
835 | (dev->iamthif_state == MEI_IAMTHIF_READING)) { | |
836 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); | |
15d4acc5 TW |
837 | |
838 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); | |
19838fb8 TW |
839 | |
840 | ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr); | |
fb7d879f OW |
841 | if (ret) |
842 | goto end; | |
fb7d879f OW |
843 | } else { |
844 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); | |
845 | ret = mei_irq_thread_read_client_message(cmpl_list, | |
846 | dev, mei_hdr); | |
847 | if (ret) | |
848 | goto end; | |
849 | ||
850 | } | |
851 | ||
852 | /* reset the number of slots and header */ | |
853 | *slots = mei_count_full_read_slots(dev); | |
854 | dev->rd_msg_hdr = 0; | |
855 | ||
856 | if (*slots == -EOVERFLOW) { | |
857 | /* overflow - reset */ | |
858 | dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); | |
859 | /* set the event since message has been read */ | |
860 | ret = -ERANGE; | |
861 | goto end; | |
862 | } | |
863 | end: | |
864 | return ret; | |
865 | } | |
866 | ||
867 | ||
868 | /** | |
869 | * mei_irq_thread_write_handler - bottom half write routine after | |
870 | * ISR to handle the write processing. | |
871 | * | |
fb7d879f | 872 | * @dev: the device structure |
9a84d616 | 873 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
874 | * |
875 | * returns 0 on success, <0 on failure. | |
876 | */ | |
9a84d616 TW |
877 | static int mei_irq_thread_write_handler(struct mei_device *dev, |
878 | struct mei_cl_cb *cmpl_list) | |
fb7d879f OW |
879 | { |
880 | ||
881 | struct mei_cl *cl; | |
b7cd2d9f | 882 | struct mei_cl_cb *pos = NULL, *next = NULL; |
fb601adb | 883 | struct mei_cl_cb *list; |
9a84d616 | 884 | s32 slots; |
fb7d879f OW |
885 | int ret; |
886 | ||
726917f0 | 887 | if (!mei_hbuf_is_empty(dev)) { |
fb7d879f OW |
888 | dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); |
889 | return 0; | |
890 | } | |
9a84d616 TW |
891 | slots = mei_hbuf_empty_slots(dev); |
892 | if (slots <= 0) | |
7d5e0e59 TW |
893 | return -EMSGSIZE; |
894 | ||
fb7d879f OW |
895 | /* complete all waiting for write CB */ |
896 | dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); | |
897 | ||
898 | list = &dev->write_waiting_list; | |
fb601adb | 899 | list_for_each_entry_safe(pos, next, &list->list, list) { |
db3ed431 | 900 | cl = pos->cl; |
b7cd2d9f TW |
901 | if (cl == NULL) |
902 | continue; | |
903 | ||
904 | cl->status = 0; | |
fb601adb | 905 | list_del(&pos->list); |
b7cd2d9f | 906 | if (MEI_WRITING == cl->writing_state && |
4b8960b4 TW |
907 | pos->fop_type == MEI_FOP_WRITE && |
908 | cl != &dev->iamthif_cl) { | |
483136ea | 909 | dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); |
b7cd2d9f | 910 | cl->writing_state = MEI_WRITE_COMPLETE; |
fb601adb | 911 | list_add_tail(&pos->list, &cmpl_list->list); |
b7cd2d9f TW |
912 | } |
913 | if (cl == &dev->iamthif_cl) { | |
914 | dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); | |
915 | if (dev->iamthif_flow_control_pending) { | |
9a84d616 | 916 | ret = mei_amthif_irq_read(dev, &slots); |
b7cd2d9f TW |
917 | if (ret) |
918 | return ret; | |
fb7d879f | 919 | } |
fb7d879f OW |
920 | } |
921 | } | |
922 | ||
c216fdeb TW |
923 | if (dev->wd_state == MEI_WD_STOPPING) { |
924 | dev->wd_state = MEI_WD_IDLE; | |
fb7d879f | 925 | wake_up_interruptible(&dev->wait_stop_wd); |
fb7d879f OW |
926 | } |
927 | ||
5fb54fb4 TW |
928 | if (dev->wr_ext_msg.hdr.length) { |
929 | mei_write_message(dev, &dev->wr_ext_msg.hdr, | |
438763f3 | 930 | dev->wr_ext_msg.data); |
9a84d616 | 931 | slots -= mei_data2slots(dev->wr_ext_msg.hdr.length); |
5fb54fb4 | 932 | dev->wr_ext_msg.hdr.length = 0; |
fb7d879f | 933 | } |
b210d750 | 934 | if (dev->dev_state == MEI_DEV_ENABLED) { |
fb7d879f | 935 | if (dev->wd_pending && |
483136ea | 936 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { |
fb7d879f OW |
937 | if (mei_wd_send(dev)) |
938 | dev_dbg(&dev->pdev->dev, "wd send failed.\n"); | |
483136ea TW |
939 | else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) |
940 | return -ENODEV; | |
fb7d879f | 941 | |
eb9af0ac | 942 | dev->wd_pending = false; |
fb7d879f | 943 | |
c216fdeb | 944 | if (dev->wd_state == MEI_WD_RUNNING) |
9a84d616 | 945 | slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); |
d242a0af | 946 | else |
9a84d616 | 947 | slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); |
fb7d879f OW |
948 | } |
949 | } | |
fb7d879f OW |
950 | |
951 | /* complete control write list CB */ | |
c8372094 | 952 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); |
fb601adb | 953 | list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) { |
db3ed431 | 954 | cl = pos->cl; |
c8372094 | 955 | if (!cl) { |
fb601adb | 956 | list_del(&pos->list); |
c8372094 TW |
957 | return -ENODEV; |
958 | } | |
4b8960b4 TW |
959 | switch (pos->fop_type) { |
960 | case MEI_FOP_CLOSE: | |
c8372094 | 961 | /* send disconnect message */ |
9a84d616 TW |
962 | ret = _mei_irq_thread_close(dev, &slots, pos, |
963 | cl, cmpl_list); | |
c8372094 TW |
964 | if (ret) |
965 | return ret; | |
fb7d879f | 966 | |
c8372094 | 967 | break; |
4b8960b4 | 968 | case MEI_FOP_READ: |
c8372094 | 969 | /* send flow control message */ |
9a84d616 TW |
970 | ret = _mei_irq_thread_read(dev, &slots, pos, |
971 | cl, cmpl_list); | |
c8372094 TW |
972 | if (ret) |
973 | return ret; | |
fb7d879f | 974 | |
c8372094 | 975 | break; |
4b8960b4 | 976 | case MEI_FOP_IOCTL: |
c8372094 | 977 | /* connect message */ |
e8cd29d8 | 978 | if (mei_other_client_is_connecting(dev, cl)) |
c8372094 | 979 | continue; |
9a84d616 TW |
980 | ret = _mei_irq_thread_ioctl(dev, &slots, pos, |
981 | cl, cmpl_list); | |
c8372094 TW |
982 | if (ret) |
983 | return ret; | |
fb7d879f | 984 | |
c8372094 | 985 | break; |
fb7d879f | 986 | |
c8372094 TW |
987 | default: |
988 | BUG(); | |
fb7d879f | 989 | } |
c8372094 | 990 | |
fb7d879f OW |
991 | } |
992 | /* complete write list CB */ | |
b7cd2d9f | 993 | dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); |
fb601adb | 994 | list_for_each_entry_safe(pos, next, &dev->write_list.list, list) { |
db3ed431 | 995 | cl = pos->cl; |
b7cd2d9f TW |
996 | if (cl == NULL) |
997 | continue; | |
be9d87a7 TW |
998 | if (mei_flow_ctrl_creds(dev, cl) <= 0) { |
999 | dev_dbg(&dev->pdev->dev, | |
1000 | "No flow control credentials for client %d, not sending.\n", | |
1001 | cl->host_client_id); | |
1002 | continue; | |
1003 | } | |
b7cd2d9f | 1004 | |
be9d87a7 | 1005 | if (cl == &dev->iamthif_cl) |
9a84d616 | 1006 | ret = mei_amthif_irq_write_complete(dev, &slots, |
24c656e5 | 1007 | pos, cmpl_list); |
be9d87a7 TW |
1008 | else |
1009 | ret = mei_irq_thread_write_complete(dev, &slots, pos, | |
1010 | cmpl_list); | |
1011 | if (ret) | |
1012 | return ret; | |
b7cd2d9f | 1013 | |
fb7d879f OW |
1014 | } |
1015 | return 0; | |
1016 | } | |
1017 | ||
1018 | ||
1019 | ||
1020 | /** | |
1021 | * mei_timer - timer function. | |
1022 | * | |
1023 | * @work: pointer to the work_struct structure | |
1024 | * | |
1025 | * NOTE: This function is called by timer interrupt work | |
1026 | */ | |
a61c6530 | 1027 | void mei_timer(struct work_struct *work) |
fb7d879f OW |
1028 | { |
1029 | unsigned long timeout; | |
1030 | struct mei_cl *cl_pos = NULL; | |
1031 | struct mei_cl *cl_next = NULL; | |
fb7d879f OW |
1032 | struct mei_cl_cb *cb_pos = NULL; |
1033 | struct mei_cl_cb *cb_next = NULL; | |
1034 | ||
1035 | struct mei_device *dev = container_of(work, | |
a61c6530 | 1036 | struct mei_device, timer_work.work); |
fb7d879f OW |
1037 | |
1038 | ||
1039 | mutex_lock(&dev->device_lock); | |
b210d750 TW |
1040 | if (dev->dev_state != MEI_DEV_ENABLED) { |
1041 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS) { | |
fb7d879f OW |
1042 | if (dev->init_clients_timer) { |
1043 | if (--dev->init_clients_timer == 0) { | |
1044 | dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n", | |
1045 | dev->init_clients_state); | |
1046 | mei_reset(dev, 1); | |
1047 | } | |
1048 | } | |
1049 | } | |
1050 | goto out; | |
1051 | } | |
1052 | /*** connect/disconnect timeouts ***/ | |
1053 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | |
1054 | if (cl_pos->timer_count) { | |
1055 | if (--cl_pos->timer_count == 0) { | |
1056 | dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n"); | |
1057 | mei_reset(dev, 1); | |
1058 | goto out; | |
1059 | } | |
1060 | } | |
1061 | } | |
1062 | ||
fb7d879f OW |
1063 | if (dev->iamthif_stall_timer) { |
1064 | if (--dev->iamthif_stall_timer == 0) { | |
32de21f7 | 1065 | dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n"); |
fb7d879f OW |
1066 | mei_reset(dev, 1); |
1067 | dev->iamthif_msg_buf_size = 0; | |
1068 | dev->iamthif_msg_buf_index = 0; | |
eb9af0ac TW |
1069 | dev->iamthif_canceled = false; |
1070 | dev->iamthif_ioctl = true; | |
fb7d879f OW |
1071 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
1072 | dev->iamthif_timer = 0; | |
1073 | ||
601a1efa TW |
1074 | mei_io_cb_free(dev->iamthif_current_cb); |
1075 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
1076 | |
1077 | dev->iamthif_file_object = NULL; | |
19838fb8 | 1078 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
1079 | } |
1080 | } | |
1081 | ||
1082 | if (dev->iamthif_timer) { | |
1083 | ||
1084 | timeout = dev->iamthif_timer + | |
3870c320 | 1085 | mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); |
fb7d879f OW |
1086 | |
1087 | dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", | |
1088 | dev->iamthif_timer); | |
1089 | dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); | |
1090 | dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); | |
1091 | if (time_after(jiffies, timeout)) { | |
1092 | /* | |
1093 | * User didn't read the AMTHI data on time (15sec) | |
1094 | * freeing AMTHI for other requests | |
1095 | */ | |
1096 | ||
1097 | dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); | |
1098 | ||
e773efc4 TW |
1099 | list_for_each_entry_safe(cb_pos, cb_next, |
1100 | &dev->amthif_rd_complete_list.list, list) { | |
fb7d879f | 1101 | |
b7cd2d9f | 1102 | cl_pos = cb_pos->file_object->private_data; |
fb7d879f | 1103 | |
b7cd2d9f TW |
1104 | /* Finding the AMTHI entry. */ |
1105 | if (cl_pos == &dev->iamthif_cl) | |
fb601adb | 1106 | list_del(&cb_pos->list); |
fb7d879f | 1107 | } |
601a1efa TW |
1108 | mei_io_cb_free(dev->iamthif_current_cb); |
1109 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
1110 | |
1111 | dev->iamthif_file_object->private_data = NULL; | |
1112 | dev->iamthif_file_object = NULL; | |
fb7d879f | 1113 | dev->iamthif_timer = 0; |
19838fb8 | 1114 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
1115 | |
1116 | } | |
1117 | } | |
1118 | out: | |
441ab50f TW |
1119 | schedule_delayed_work(&dev->timer_work, 2 * HZ); |
1120 | mutex_unlock(&dev->device_lock); | |
fb7d879f OW |
1121 | } |
1122 | ||
1123 | /** | |
1124 | * mei_interrupt_thread_handler - function called after ISR to handle the interrupt | |
1125 | * processing. | |
1126 | * | |
1127 | * @irq: The irq number | |
1128 | * @dev_id: pointer to the device structure | |
1129 | * | |
1130 | * returns irqreturn_t | |
1131 | * | |
1132 | */ | |
1133 | irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) | |
1134 | { | |
1135 | struct mei_device *dev = (struct mei_device *) dev_id; | |
fb601adb | 1136 | struct mei_cl_cb complete_list; |
fb7d879f OW |
1137 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; |
1138 | struct mei_cl *cl; | |
1139 | s32 slots; | |
1140 | int rets; | |
1141 | bool bus_message_received; | |
1142 | ||
1143 | ||
1144 | dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); | |
1145 | /* initialize our complete list */ | |
1146 | mutex_lock(&dev->device_lock); | |
0288c7c9 | 1147 | mei_io_list_init(&complete_list); |
fb7d879f | 1148 | dev->host_hw_state = mei_hcsr_read(dev); |
4f61a7ad TW |
1149 | |
1150 | /* Ack the interrupt here | |
5f9092f3 | 1151 | * In case of MSI we don't go through the quick handler */ |
4f61a7ad TW |
1152 | if (pci_dev_msi_enabled(dev->pdev)) |
1153 | mei_reg_write(dev, H_CSR, dev->host_hw_state); | |
1154 | ||
fb7d879f OW |
1155 | dev->me_hw_state = mei_mecsr_read(dev); |
1156 | ||
1157 | /* check if ME wants a reset */ | |
1158 | if ((dev->me_hw_state & ME_RDY_HRA) == 0 && | |
b210d750 TW |
1159 | dev->dev_state != MEI_DEV_RESETING && |
1160 | dev->dev_state != MEI_DEV_INITIALIZING) { | |
fb7d879f OW |
1161 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); |
1162 | mei_reset(dev, 1); | |
1163 | mutex_unlock(&dev->device_lock); | |
1164 | return IRQ_HANDLED; | |
1165 | } | |
1166 | ||
1167 | /* check if we need to start the dev */ | |
1168 | if ((dev->host_hw_state & H_RDY) == 0) { | |
1169 | if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) { | |
1170 | dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); | |
1171 | dev->host_hw_state |= (H_IE | H_IG | H_RDY); | |
1172 | mei_hcsr_set(dev); | |
b210d750 | 1173 | dev->dev_state = MEI_DEV_INIT_CLIENTS; |
fb7d879f OW |
1174 | dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); |
1175 | /* link is established | |
1176 | * start sending messages. | |
1177 | */ | |
c95efb74 | 1178 | mei_host_start_message(dev); |
fb7d879f OW |
1179 | mutex_unlock(&dev->device_lock); |
1180 | return IRQ_HANDLED; | |
1181 | } else { | |
1182 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); | |
1183 | mutex_unlock(&dev->device_lock); | |
1184 | return IRQ_HANDLED; | |
1185 | } | |
1186 | } | |
5f9092f3 | 1187 | /* check slots available for reading */ |
fb7d879f | 1188 | slots = mei_count_full_read_slots(dev); |
5fb54fb4 TW |
1189 | while (slots > 0) { |
1190 | /* we have urgent data to send so break the read */ | |
1191 | if (dev->wr_ext_msg.hdr.length) | |
1192 | break; | |
1193 | dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots); | |
fb7d879f OW |
1194 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n"); |
1195 | rets = mei_irq_thread_read_handler(&complete_list, dev, &slots); | |
1196 | if (rets) | |
1197 | goto end; | |
1198 | } | |
9a84d616 | 1199 | rets = mei_irq_thread_write_handler(dev, &complete_list); |
fb7d879f OW |
1200 | end: |
1201 | dev_dbg(&dev->pdev->dev, "end of bottom half function.\n"); | |
1202 | dev->host_hw_state = mei_hcsr_read(dev); | |
726917f0 | 1203 | dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev); |
fb7d879f OW |
1204 | |
1205 | bus_message_received = false; | |
1206 | if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { | |
1207 | dev_dbg(&dev->pdev->dev, "received waiting bus message\n"); | |
1208 | bus_message_received = true; | |
1209 | } | |
1210 | mutex_unlock(&dev->device_lock); | |
1211 | if (bus_message_received) { | |
1212 | dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n"); | |
1213 | wake_up_interruptible(&dev->wait_recvd_msg); | |
1214 | bus_message_received = false; | |
1215 | } | |
fb601adb | 1216 | if (list_empty(&complete_list.list)) |
fb7d879f OW |
1217 | return IRQ_HANDLED; |
1218 | ||
1219 | ||
fb601adb | 1220 | list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { |
db3ed431 | 1221 | cl = cb_pos->cl; |
fb601adb | 1222 | list_del(&cb_pos->list); |
fb7d879f OW |
1223 | if (cl) { |
1224 | if (cl != &dev->iamthif_cl) { | |
1225 | dev_dbg(&dev->pdev->dev, "completing call back.\n"); | |
1226 | _mei_cmpl(cl, cb_pos); | |
1227 | cb_pos = NULL; | |
1228 | } else if (cl == &dev->iamthif_cl) { | |
19838fb8 | 1229 | mei_amthif_complete(dev, cb_pos); |
fb7d879f OW |
1230 | } |
1231 | } | |
1232 | } | |
1233 | return IRQ_HANDLED; | |
1234 | } |