]>
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 | ||
4f3afe1d | 24 | #include <linux/mei.h> |
47a73801 TW |
25 | |
26 | #include "mei_dev.h" | |
fb7d879f OW |
27 | #include "interface.h" |
28 | ||
29 | ||
fb7d879f OW |
30 | /** |
31 | * _mei_cmpl - processes completed operation. | |
32 | * | |
33 | * @cl: private data of the file object. | |
34 | * @cb_pos: callback block. | |
35 | */ | |
36 | static void _mei_cmpl(struct mei_cl *cl, struct mei_cl_cb *cb_pos) | |
37 | { | |
4b8960b4 | 38 | if (cb_pos->fop_type == MEI_FOP_WRITE) { |
601a1efa | 39 | mei_io_cb_free(cb_pos); |
fb7d879f OW |
40 | cb_pos = NULL; |
41 | cl->writing_state = MEI_WRITE_COMPLETE; | |
42 | if (waitqueue_active(&cl->tx_wait)) | |
43 | wake_up_interruptible(&cl->tx_wait); | |
44 | ||
4b8960b4 | 45 | } else if (cb_pos->fop_type == MEI_FOP_READ && |
fb7d879f OW |
46 | MEI_READING == cl->reading_state) { |
47 | cl->reading_state = MEI_READ_COMPLETE; | |
48 | if (waitqueue_active(&cl->rx_wait)) | |
49 | wake_up_interruptible(&cl->rx_wait); | |
50 | ||
51 | } | |
52 | } | |
53 | ||
fb7d879f OW |
54 | /** |
55 | * _mei_irq_thread_state_ok - checks if mei header matches file private data | |
56 | * | |
57 | * @cl: private data of the file object | |
58 | * @mei_hdr: header of mei client message | |
59 | * | |
60 | * returns !=0 if matches, 0 if no match. | |
61 | */ | |
62 | static int _mei_irq_thread_state_ok(struct mei_cl *cl, | |
63 | struct mei_msg_hdr *mei_hdr) | |
64 | { | |
65 | return (cl->host_client_id == mei_hdr->host_addr && | |
66 | cl->me_client_id == mei_hdr->me_addr && | |
67 | cl->state == MEI_FILE_CONNECTED && | |
68 | MEI_READ_COMPLETE != cl->reading_state); | |
69 | } | |
70 | ||
71 | /** | |
72 | * mei_irq_thread_read_client_message - bottom half read routine after ISR to | |
73 | * handle the read mei client message data processing. | |
74 | * | |
75 | * @complete_list: An instance of our list structure | |
76 | * @dev: the device structure | |
77 | * @mei_hdr: header of mei client message | |
78 | * | |
79 | * returns 0 on success, <0 on failure. | |
80 | */ | |
fb601adb | 81 | static int mei_irq_thread_read_client_message(struct mei_cl_cb *complete_list, |
fb7d879f OW |
82 | struct mei_device *dev, |
83 | struct mei_msg_hdr *mei_hdr) | |
84 | { | |
85 | struct mei_cl *cl; | |
86 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; | |
479bc59d | 87 | unsigned char *buffer = NULL; |
fb7d879f OW |
88 | |
89 | dev_dbg(&dev->pdev->dev, "start client msg\n"); | |
fb601adb | 90 | if (list_empty(&dev->read_list.list)) |
fb7d879f OW |
91 | goto quit; |
92 | ||
fb601adb | 93 | list_for_each_entry_safe(cb_pos, cb_next, &dev->read_list.list, list) { |
db3ed431 | 94 | cl = cb_pos->cl; |
fb7d879f OW |
95 | if (cl && _mei_irq_thread_state_ok(cl, mei_hdr)) { |
96 | cl->reading_state = MEI_READING; | |
ebb108ef | 97 | buffer = cb_pos->response_buffer.data + cb_pos->buf_idx; |
fb7d879f OW |
98 | |
99 | if (cb_pos->response_buffer.size < | |
ebb108ef | 100 | mei_hdr->length + cb_pos->buf_idx) { |
fb7d879f | 101 | dev_dbg(&dev->pdev->dev, "message overflow.\n"); |
fb601adb | 102 | list_del(&cb_pos->list); |
fb7d879f OW |
103 | return -ENOMEM; |
104 | } | |
105 | if (buffer) | |
106 | mei_read_slots(dev, buffer, mei_hdr->length); | |
107 | ||
ebb108ef | 108 | cb_pos->buf_idx += mei_hdr->length; |
fb7d879f OW |
109 | if (mei_hdr->msg_complete) { |
110 | cl->status = 0; | |
fb601adb | 111 | list_del(&cb_pos->list); |
fb7d879f | 112 | dev_dbg(&dev->pdev->dev, |
a4136b49 | 113 | "completed read H cl = %d, ME cl = %d, length = %lu\n", |
fb7d879f OW |
114 | cl->host_client_id, |
115 | cl->me_client_id, | |
ebb108ef TW |
116 | cb_pos->buf_idx); |
117 | ||
fb601adb TW |
118 | list_add_tail(&cb_pos->list, |
119 | &complete_list->list); | |
fb7d879f OW |
120 | } |
121 | ||
122 | break; | |
123 | } | |
124 | ||
125 | } | |
126 | ||
127 | quit: | |
128 | dev_dbg(&dev->pdev->dev, "message read\n"); | |
129 | if (!buffer) { | |
edf1eed4 | 130 | mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); |
15d4acc5 TW |
131 | dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n", |
132 | MEI_HDR_PRM(mei_hdr)); | |
fb7d879f OW |
133 | } |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
fb7d879f OW |
138 | /** |
139 | * _mei_irq_thread_close - processes close related operation. | |
140 | * | |
141 | * @dev: the device structure. | |
142 | * @slots: free slots. | |
143 | * @cb_pos: callback block. | |
144 | * @cl: private data of the file object. | |
145 | * @cmpl_list: complete list. | |
146 | * | |
147 | * returns 0, OK; otherwise, error. | |
148 | */ | |
149 | static int _mei_irq_thread_close(struct mei_device *dev, s32 *slots, | |
150 | struct mei_cl_cb *cb_pos, | |
151 | struct mei_cl *cl, | |
fb601adb | 152 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 153 | { |
b45f3ccf | 154 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
aeba4a06 | 155 | sizeof(struct hbm_client_connect_request))) |
b45f3ccf | 156 | return -EBADMSG; |
fb7d879f | 157 | |
aeba4a06 | 158 | *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); |
b45f3ccf | 159 | |
8120e720 | 160 | if (mei_hbm_cl_disconnect_req(dev, cl)) { |
b45f3ccf | 161 | cl->status = 0; |
ebb108ef | 162 | cb_pos->buf_idx = 0; |
fb601adb | 163 | list_move_tail(&cb_pos->list, &cmpl_list->list); |
b45f3ccf | 164 | return -EMSGSIZE; |
fb7d879f | 165 | } else { |
b45f3ccf TW |
166 | cl->state = MEI_FILE_DISCONNECTING; |
167 | cl->status = 0; | |
ebb108ef | 168 | cb_pos->buf_idx = 0; |
fb601adb | 169 | list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); |
b45f3ccf | 170 | cl->timer_count = MEI_CONNECT_TIMEOUT; |
fb7d879f OW |
171 | } |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
fb7d879f OW |
176 | |
177 | /** | |
178 | * _mei_hb_read - processes read related operation. | |
179 | * | |
180 | * @dev: the device structure. | |
181 | * @slots: free slots. | |
182 | * @cb_pos: callback block. | |
183 | * @cl: private data of the file object. | |
184 | * @cmpl_list: complete list. | |
185 | * | |
186 | * returns 0, OK; otherwise, error. | |
187 | */ | |
188 | static int _mei_irq_thread_read(struct mei_device *dev, s32 *slots, | |
189 | struct mei_cl_cb *cb_pos, | |
190 | struct mei_cl *cl, | |
fb601adb | 191 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 192 | { |
1e69d64a | 193 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
fb7d879f | 194 | sizeof(struct hbm_flow_control))) { |
fb7d879f | 195 | /* return the cancel routine */ |
fb601adb | 196 | list_del(&cb_pos->list); |
fb7d879f OW |
197 | return -EBADMSG; |
198 | } | |
199 | ||
7bdf72d3 TW |
200 | *slots -= mei_data2slots(sizeof(struct hbm_flow_control)); |
201 | ||
8120e720 | 202 | if (mei_hbm_cl_flow_control_req(dev, cl)) { |
1ccb7b62 | 203 | cl->status = -ENODEV; |
ebb108ef | 204 | cb_pos->buf_idx = 0; |
fb601adb | 205 | list_move_tail(&cb_pos->list, &cmpl_list->list); |
1ccb7b62 TW |
206 | return -ENODEV; |
207 | } | |
fb601adb | 208 | list_move_tail(&cb_pos->list, &dev->read_list.list); |
1ccb7b62 | 209 | |
fb7d879f OW |
210 | return 0; |
211 | } | |
212 | ||
213 | ||
214 | /** | |
215 | * _mei_irq_thread_ioctl - processes ioctl related operation. | |
216 | * | |
217 | * @dev: the device structure. | |
218 | * @slots: free slots. | |
219 | * @cb_pos: callback block. | |
220 | * @cl: private data of the file object. | |
221 | * @cmpl_list: complete list. | |
222 | * | |
223 | * returns 0, OK; otherwise, error. | |
224 | */ | |
225 | static int _mei_irq_thread_ioctl(struct mei_device *dev, s32 *slots, | |
226 | struct mei_cl_cb *cb_pos, | |
227 | struct mei_cl *cl, | |
fb601adb | 228 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 229 | { |
b45f3ccf | 230 | if ((*slots * sizeof(u32)) < (sizeof(struct mei_msg_hdr) + |
fb7d879f | 231 | sizeof(struct hbm_client_connect_request))) { |
fb7d879f | 232 | /* return the cancel routine */ |
fb601adb | 233 | list_del(&cb_pos->list); |
fb7d879f OW |
234 | return -EBADMSG; |
235 | } | |
236 | ||
b45f3ccf | 237 | cl->state = MEI_FILE_CONNECTING; |
8120e720 TW |
238 | *slots -= mei_data2slots(sizeof(struct hbm_client_connect_request)); |
239 | if (mei_hbm_cl_connect_req(dev, cl)) { | |
b45f3ccf | 240 | cl->status = -ENODEV; |
ebb108ef | 241 | cb_pos->buf_idx = 0; |
fb601adb | 242 | list_del(&cb_pos->list); |
b45f3ccf TW |
243 | return -ENODEV; |
244 | } else { | |
fb601adb | 245 | list_move_tail(&cb_pos->list, &dev->ctrl_rd_list.list); |
b45f3ccf TW |
246 | cl->timer_count = MEI_CONNECT_TIMEOUT; |
247 | } | |
fb7d879f OW |
248 | return 0; |
249 | } | |
250 | ||
251 | /** | |
ea3b5fb7 | 252 | * mei_irq_thread_write_complete - write messages to device. |
fb7d879f OW |
253 | * |
254 | * @dev: the device structure. | |
255 | * @slots: free slots. | |
ea3b5fb7 | 256 | * @cb: callback block. |
fb7d879f OW |
257 | * @cmpl_list: complete list. |
258 | * | |
259 | * returns 0, OK; otherwise, error. | |
260 | */ | |
ea3b5fb7 TW |
261 | static int mei_irq_thread_write_complete(struct mei_device *dev, s32 *slots, |
262 | struct mei_cl_cb *cb, struct mei_cl_cb *cmpl_list) | |
fb7d879f | 263 | { |
e46f1874 | 264 | struct mei_msg_hdr mei_hdr; |
ea3b5fb7 TW |
265 | struct mei_cl *cl = cb->cl; |
266 | size_t len = cb->request_buffer.size - cb->buf_idx; | |
267 | size_t msg_slots = mei_data2slots(len); | |
268 | ||
e46f1874 TW |
269 | mei_hdr.host_addr = cl->host_client_id; |
270 | mei_hdr.me_addr = cl->me_client_id; | |
271 | mei_hdr.reserved = 0; | |
fb7d879f | 272 | |
ea3b5fb7 | 273 | if (*slots >= msg_slots) { |
e46f1874 TW |
274 | mei_hdr.length = len; |
275 | mei_hdr.msg_complete = 1; | |
ea3b5fb7 | 276 | /* Split the message only if we can write the whole host buffer */ |
24aadc80 | 277 | } else if (*slots == dev->hbuf_depth) { |
ea3b5fb7 TW |
278 | msg_slots = *slots; |
279 | len = (*slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | |
e46f1874 TW |
280 | mei_hdr.length = len; |
281 | mei_hdr.msg_complete = 0; | |
fb7d879f | 282 | } else { |
ea3b5fb7 TW |
283 | /* wait for next time the host buffer is empty */ |
284 | return 0; | |
fb7d879f OW |
285 | } |
286 | ||
ea3b5fb7 TW |
287 | dev_dbg(&dev->pdev->dev, "buf: size = %d idx = %lu\n", |
288 | cb->request_buffer.size, cb->buf_idx); | |
e46f1874 | 289 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); |
ea3b5fb7 TW |
290 | |
291 | *slots -= msg_slots; | |
e46f1874 | 292 | if (mei_write_message(dev, &mei_hdr, |
438763f3 | 293 | cb->request_buffer.data + cb->buf_idx)) { |
ea3b5fb7 TW |
294 | cl->status = -ENODEV; |
295 | list_move_tail(&cb->list, &cmpl_list->list); | |
296 | return -ENODEV; | |
297 | } | |
298 | ||
299 | if (mei_flow_ctrl_reduce(dev, cl)) | |
300 | return -ENODEV; | |
301 | ||
302 | cl->status = 0; | |
e46f1874 TW |
303 | cb->buf_idx += mei_hdr.length; |
304 | if (mei_hdr.msg_complete) | |
ea3b5fb7 TW |
305 | list_move_tail(&cb->list, &dev->write_waiting_list.list); |
306 | ||
fb7d879f OW |
307 | return 0; |
308 | } | |
309 | ||
fb7d879f OW |
310 | /** |
311 | * mei_irq_thread_read_handler - bottom half read routine after ISR to | |
312 | * handle the read processing. | |
313 | * | |
314 | * @cmpl_list: An instance of our list structure | |
315 | * @dev: the device structure | |
316 | * @slots: slots to read. | |
317 | * | |
318 | * returns 0 on success, <0 on failure. | |
319 | */ | |
fb601adb | 320 | static int mei_irq_thread_read_handler(struct mei_cl_cb *cmpl_list, |
fb7d879f OW |
321 | struct mei_device *dev, |
322 | s32 *slots) | |
323 | { | |
324 | struct mei_msg_hdr *mei_hdr; | |
325 | struct mei_cl *cl_pos = NULL; | |
326 | struct mei_cl *cl_next = NULL; | |
327 | int ret = 0; | |
328 | ||
329 | if (!dev->rd_msg_hdr) { | |
330 | dev->rd_msg_hdr = mei_mecbrw_read(dev); | |
331 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | |
332 | (*slots)--; | |
333 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | |
334 | } | |
335 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | |
15d4acc5 | 336 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
fb7d879f OW |
337 | |
338 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | |
339 | dev_dbg(&dev->pdev->dev, "corrupted message header.\n"); | |
340 | ret = -EBADMSG; | |
341 | goto end; | |
342 | } | |
343 | ||
344 | if (mei_hdr->host_addr || mei_hdr->me_addr) { | |
345 | list_for_each_entry_safe(cl_pos, cl_next, | |
346 | &dev->file_list, link) { | |
347 | dev_dbg(&dev->pdev->dev, | |
348 | "list_for_each_entry_safe read host" | |
349 | " client = %d, ME client = %d\n", | |
350 | cl_pos->host_client_id, | |
351 | cl_pos->me_client_id); | |
352 | if (cl_pos->host_client_id == mei_hdr->host_addr && | |
353 | cl_pos->me_client_id == mei_hdr->me_addr) | |
354 | break; | |
355 | } | |
356 | ||
357 | if (&cl_pos->link == &dev->file_list) { | |
358 | dev_dbg(&dev->pdev->dev, "corrupted message header\n"); | |
359 | ret = -EBADMSG; | |
360 | goto end; | |
361 | } | |
362 | } | |
363 | if (((*slots) * sizeof(u32)) < mei_hdr->length) { | |
364 | dev_dbg(&dev->pdev->dev, | |
365 | "we can't read the message slots =%08x.\n", | |
366 | *slots); | |
367 | /* we can't read the message */ | |
368 | ret = -ERANGE; | |
369 | goto end; | |
370 | } | |
371 | ||
372 | /* decide where to read the message too */ | |
373 | if (!mei_hdr->host_addr) { | |
374 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_bus_message.\n"); | |
bb1b0133 | 375 | mei_hbm_dispatch(dev, mei_hdr); |
fb7d879f OW |
376 | dev_dbg(&dev->pdev->dev, "end mei_irq_thread_read_bus_message.\n"); |
377 | } else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && | |
378 | (MEI_FILE_CONNECTED == dev->iamthif_cl.state) && | |
379 | (dev->iamthif_state == MEI_IAMTHIF_READING)) { | |
380 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_iamthif_message.\n"); | |
15d4acc5 TW |
381 | |
382 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); | |
19838fb8 TW |
383 | |
384 | ret = mei_amthif_irq_read_message(cmpl_list, dev, mei_hdr); | |
fb7d879f OW |
385 | if (ret) |
386 | goto end; | |
fb7d879f OW |
387 | } else { |
388 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_client_message.\n"); | |
389 | ret = mei_irq_thread_read_client_message(cmpl_list, | |
390 | dev, mei_hdr); | |
391 | if (ret) | |
392 | goto end; | |
393 | ||
394 | } | |
395 | ||
396 | /* reset the number of slots and header */ | |
397 | *slots = mei_count_full_read_slots(dev); | |
398 | dev->rd_msg_hdr = 0; | |
399 | ||
400 | if (*slots == -EOVERFLOW) { | |
401 | /* overflow - reset */ | |
402 | dev_dbg(&dev->pdev->dev, "resetting due to slots overflow.\n"); | |
403 | /* set the event since message has been read */ | |
404 | ret = -ERANGE; | |
405 | goto end; | |
406 | } | |
407 | end: | |
408 | return ret; | |
409 | } | |
410 | ||
411 | ||
412 | /** | |
413 | * mei_irq_thread_write_handler - bottom half write routine after | |
414 | * ISR to handle the write processing. | |
415 | * | |
fb7d879f | 416 | * @dev: the device structure |
9a84d616 | 417 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
418 | * |
419 | * returns 0 on success, <0 on failure. | |
420 | */ | |
9a84d616 TW |
421 | static int mei_irq_thread_write_handler(struct mei_device *dev, |
422 | struct mei_cl_cb *cmpl_list) | |
fb7d879f OW |
423 | { |
424 | ||
425 | struct mei_cl *cl; | |
b7cd2d9f | 426 | struct mei_cl_cb *pos = NULL, *next = NULL; |
fb601adb | 427 | struct mei_cl_cb *list; |
9a84d616 | 428 | s32 slots; |
fb7d879f OW |
429 | int ret; |
430 | ||
726917f0 | 431 | if (!mei_hbuf_is_empty(dev)) { |
fb7d879f OW |
432 | dev_dbg(&dev->pdev->dev, "host buffer is not empty.\n"); |
433 | return 0; | |
434 | } | |
9a84d616 TW |
435 | slots = mei_hbuf_empty_slots(dev); |
436 | if (slots <= 0) | |
7d5e0e59 TW |
437 | return -EMSGSIZE; |
438 | ||
fb7d879f OW |
439 | /* complete all waiting for write CB */ |
440 | dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); | |
441 | ||
442 | list = &dev->write_waiting_list; | |
fb601adb | 443 | list_for_each_entry_safe(pos, next, &list->list, list) { |
db3ed431 | 444 | cl = pos->cl; |
b7cd2d9f TW |
445 | if (cl == NULL) |
446 | continue; | |
447 | ||
448 | cl->status = 0; | |
fb601adb | 449 | list_del(&pos->list); |
b7cd2d9f | 450 | if (MEI_WRITING == cl->writing_state && |
4b8960b4 TW |
451 | pos->fop_type == MEI_FOP_WRITE && |
452 | cl != &dev->iamthif_cl) { | |
483136ea | 453 | dev_dbg(&dev->pdev->dev, "MEI WRITE COMPLETE\n"); |
b7cd2d9f | 454 | cl->writing_state = MEI_WRITE_COMPLETE; |
fb601adb | 455 | list_add_tail(&pos->list, &cmpl_list->list); |
b7cd2d9f TW |
456 | } |
457 | if (cl == &dev->iamthif_cl) { | |
458 | dev_dbg(&dev->pdev->dev, "check iamthif flow control.\n"); | |
459 | if (dev->iamthif_flow_control_pending) { | |
9a84d616 | 460 | ret = mei_amthif_irq_read(dev, &slots); |
b7cd2d9f TW |
461 | if (ret) |
462 | return ret; | |
fb7d879f | 463 | } |
fb7d879f OW |
464 | } |
465 | } | |
466 | ||
c216fdeb TW |
467 | if (dev->wd_state == MEI_WD_STOPPING) { |
468 | dev->wd_state = MEI_WD_IDLE; | |
fb7d879f | 469 | wake_up_interruptible(&dev->wait_stop_wd); |
fb7d879f OW |
470 | } |
471 | ||
5fb54fb4 TW |
472 | if (dev->wr_ext_msg.hdr.length) { |
473 | mei_write_message(dev, &dev->wr_ext_msg.hdr, | |
438763f3 | 474 | dev->wr_ext_msg.data); |
9a84d616 | 475 | slots -= mei_data2slots(dev->wr_ext_msg.hdr.length); |
5fb54fb4 | 476 | dev->wr_ext_msg.hdr.length = 0; |
fb7d879f | 477 | } |
b210d750 | 478 | if (dev->dev_state == MEI_DEV_ENABLED) { |
fb7d879f | 479 | if (dev->wd_pending && |
483136ea | 480 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { |
fb7d879f OW |
481 | if (mei_wd_send(dev)) |
482 | dev_dbg(&dev->pdev->dev, "wd send failed.\n"); | |
483136ea TW |
483 | else if (mei_flow_ctrl_reduce(dev, &dev->wd_cl)) |
484 | return -ENODEV; | |
fb7d879f | 485 | |
eb9af0ac | 486 | dev->wd_pending = false; |
fb7d879f | 487 | |
c216fdeb | 488 | if (dev->wd_state == MEI_WD_RUNNING) |
9a84d616 | 489 | slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); |
d242a0af | 490 | else |
9a84d616 | 491 | slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); |
fb7d879f OW |
492 | } |
493 | } | |
fb7d879f OW |
494 | |
495 | /* complete control write list CB */ | |
c8372094 | 496 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); |
fb601adb | 497 | list_for_each_entry_safe(pos, next, &dev->ctrl_wr_list.list, list) { |
db3ed431 | 498 | cl = pos->cl; |
c8372094 | 499 | if (!cl) { |
fb601adb | 500 | list_del(&pos->list); |
c8372094 TW |
501 | return -ENODEV; |
502 | } | |
4b8960b4 TW |
503 | switch (pos->fop_type) { |
504 | case MEI_FOP_CLOSE: | |
c8372094 | 505 | /* send disconnect message */ |
9a84d616 TW |
506 | ret = _mei_irq_thread_close(dev, &slots, pos, |
507 | cl, cmpl_list); | |
c8372094 TW |
508 | if (ret) |
509 | return ret; | |
fb7d879f | 510 | |
c8372094 | 511 | break; |
4b8960b4 | 512 | case MEI_FOP_READ: |
c8372094 | 513 | /* send flow control message */ |
9a84d616 TW |
514 | ret = _mei_irq_thread_read(dev, &slots, pos, |
515 | cl, cmpl_list); | |
c8372094 TW |
516 | if (ret) |
517 | return ret; | |
fb7d879f | 518 | |
c8372094 | 519 | break; |
4b8960b4 | 520 | case MEI_FOP_IOCTL: |
c8372094 | 521 | /* connect message */ |
e8cd29d8 | 522 | if (mei_other_client_is_connecting(dev, cl)) |
c8372094 | 523 | continue; |
9a84d616 TW |
524 | ret = _mei_irq_thread_ioctl(dev, &slots, pos, |
525 | cl, cmpl_list); | |
c8372094 TW |
526 | if (ret) |
527 | return ret; | |
fb7d879f | 528 | |
c8372094 | 529 | break; |
fb7d879f | 530 | |
c8372094 TW |
531 | default: |
532 | BUG(); | |
fb7d879f | 533 | } |
c8372094 | 534 | |
fb7d879f OW |
535 | } |
536 | /* complete write list CB */ | |
b7cd2d9f | 537 | dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); |
fb601adb | 538 | list_for_each_entry_safe(pos, next, &dev->write_list.list, list) { |
db3ed431 | 539 | cl = pos->cl; |
b7cd2d9f TW |
540 | if (cl == NULL) |
541 | continue; | |
be9d87a7 TW |
542 | if (mei_flow_ctrl_creds(dev, cl) <= 0) { |
543 | dev_dbg(&dev->pdev->dev, | |
544 | "No flow control credentials for client %d, not sending.\n", | |
545 | cl->host_client_id); | |
546 | continue; | |
547 | } | |
b7cd2d9f | 548 | |
be9d87a7 | 549 | if (cl == &dev->iamthif_cl) |
9a84d616 | 550 | ret = mei_amthif_irq_write_complete(dev, &slots, |
24c656e5 | 551 | pos, cmpl_list); |
be9d87a7 TW |
552 | else |
553 | ret = mei_irq_thread_write_complete(dev, &slots, pos, | |
554 | cmpl_list); | |
555 | if (ret) | |
556 | return ret; | |
b7cd2d9f | 557 | |
fb7d879f OW |
558 | } |
559 | return 0; | |
560 | } | |
561 | ||
562 | ||
563 | ||
564 | /** | |
565 | * mei_timer - timer function. | |
566 | * | |
567 | * @work: pointer to the work_struct structure | |
568 | * | |
569 | * NOTE: This function is called by timer interrupt work | |
570 | */ | |
a61c6530 | 571 | void mei_timer(struct work_struct *work) |
fb7d879f OW |
572 | { |
573 | unsigned long timeout; | |
574 | struct mei_cl *cl_pos = NULL; | |
575 | struct mei_cl *cl_next = NULL; | |
fb7d879f OW |
576 | struct mei_cl_cb *cb_pos = NULL; |
577 | struct mei_cl_cb *cb_next = NULL; | |
578 | ||
579 | struct mei_device *dev = container_of(work, | |
a61c6530 | 580 | struct mei_device, timer_work.work); |
fb7d879f OW |
581 | |
582 | ||
583 | mutex_lock(&dev->device_lock); | |
b210d750 TW |
584 | if (dev->dev_state != MEI_DEV_ENABLED) { |
585 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS) { | |
fb7d879f OW |
586 | if (dev->init_clients_timer) { |
587 | if (--dev->init_clients_timer == 0) { | |
588 | dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n", | |
589 | dev->init_clients_state); | |
590 | mei_reset(dev, 1); | |
591 | } | |
592 | } | |
593 | } | |
594 | goto out; | |
595 | } | |
596 | /*** connect/disconnect timeouts ***/ | |
597 | list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { | |
598 | if (cl_pos->timer_count) { | |
599 | if (--cl_pos->timer_count == 0) { | |
600 | dev_dbg(&dev->pdev->dev, "HECI reset due to connect/disconnect timeout.\n"); | |
601 | mei_reset(dev, 1); | |
602 | goto out; | |
603 | } | |
604 | } | |
605 | } | |
606 | ||
fb7d879f OW |
607 | if (dev->iamthif_stall_timer) { |
608 | if (--dev->iamthif_stall_timer == 0) { | |
32de21f7 | 609 | dev_dbg(&dev->pdev->dev, "resetting because of hang to amthi.\n"); |
fb7d879f OW |
610 | mei_reset(dev, 1); |
611 | dev->iamthif_msg_buf_size = 0; | |
612 | dev->iamthif_msg_buf_index = 0; | |
eb9af0ac TW |
613 | dev->iamthif_canceled = false; |
614 | dev->iamthif_ioctl = true; | |
fb7d879f OW |
615 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
616 | dev->iamthif_timer = 0; | |
617 | ||
601a1efa TW |
618 | mei_io_cb_free(dev->iamthif_current_cb); |
619 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
620 | |
621 | dev->iamthif_file_object = NULL; | |
19838fb8 | 622 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
623 | } |
624 | } | |
625 | ||
626 | if (dev->iamthif_timer) { | |
627 | ||
628 | timeout = dev->iamthif_timer + | |
3870c320 | 629 | mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); |
fb7d879f OW |
630 | |
631 | dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", | |
632 | dev->iamthif_timer); | |
633 | dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); | |
634 | dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); | |
635 | if (time_after(jiffies, timeout)) { | |
636 | /* | |
637 | * User didn't read the AMTHI data on time (15sec) | |
638 | * freeing AMTHI for other requests | |
639 | */ | |
640 | ||
641 | dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); | |
642 | ||
e773efc4 TW |
643 | list_for_each_entry_safe(cb_pos, cb_next, |
644 | &dev->amthif_rd_complete_list.list, list) { | |
fb7d879f | 645 | |
b7cd2d9f | 646 | cl_pos = cb_pos->file_object->private_data; |
fb7d879f | 647 | |
b7cd2d9f TW |
648 | /* Finding the AMTHI entry. */ |
649 | if (cl_pos == &dev->iamthif_cl) | |
fb601adb | 650 | list_del(&cb_pos->list); |
fb7d879f | 651 | } |
601a1efa TW |
652 | mei_io_cb_free(dev->iamthif_current_cb); |
653 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
654 | |
655 | dev->iamthif_file_object->private_data = NULL; | |
656 | dev->iamthif_file_object = NULL; | |
fb7d879f | 657 | dev->iamthif_timer = 0; |
19838fb8 | 658 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
659 | |
660 | } | |
661 | } | |
662 | out: | |
441ab50f TW |
663 | schedule_delayed_work(&dev->timer_work, 2 * HZ); |
664 | mutex_unlock(&dev->device_lock); | |
fb7d879f OW |
665 | } |
666 | ||
667 | /** | |
668 | * mei_interrupt_thread_handler - function called after ISR to handle the interrupt | |
669 | * processing. | |
670 | * | |
671 | * @irq: The irq number | |
672 | * @dev_id: pointer to the device structure | |
673 | * | |
674 | * returns irqreturn_t | |
675 | * | |
676 | */ | |
677 | irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id) | |
678 | { | |
679 | struct mei_device *dev = (struct mei_device *) dev_id; | |
fb601adb | 680 | struct mei_cl_cb complete_list; |
fb7d879f OW |
681 | struct mei_cl_cb *cb_pos = NULL, *cb_next = NULL; |
682 | struct mei_cl *cl; | |
683 | s32 slots; | |
684 | int rets; | |
685 | bool bus_message_received; | |
686 | ||
687 | ||
688 | dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n"); | |
689 | /* initialize our complete list */ | |
690 | mutex_lock(&dev->device_lock); | |
0288c7c9 | 691 | mei_io_list_init(&complete_list); |
fb7d879f | 692 | dev->host_hw_state = mei_hcsr_read(dev); |
4f61a7ad TW |
693 | |
694 | /* Ack the interrupt here | |
5f9092f3 | 695 | * In case of MSI we don't go through the quick handler */ |
4f61a7ad | 696 | if (pci_dev_msi_enabled(dev->pdev)) |
3a65dd4e | 697 | mei_clear_interrupts(dev); |
4f61a7ad | 698 | |
fb7d879f OW |
699 | dev->me_hw_state = mei_mecsr_read(dev); |
700 | ||
701 | /* check if ME wants a reset */ | |
702 | if ((dev->me_hw_state & ME_RDY_HRA) == 0 && | |
b210d750 TW |
703 | dev->dev_state != MEI_DEV_RESETING && |
704 | dev->dev_state != MEI_DEV_INITIALIZING) { | |
fb7d879f OW |
705 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); |
706 | mei_reset(dev, 1); | |
707 | mutex_unlock(&dev->device_lock); | |
708 | return IRQ_HANDLED; | |
709 | } | |
710 | ||
711 | /* check if we need to start the dev */ | |
712 | if ((dev->host_hw_state & H_RDY) == 0) { | |
713 | if ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA) { | |
714 | dev_dbg(&dev->pdev->dev, "we need to start the dev.\n"); | |
715 | dev->host_hw_state |= (H_IE | H_IG | H_RDY); | |
716 | mei_hcsr_set(dev); | |
b210d750 | 717 | dev->dev_state = MEI_DEV_INIT_CLIENTS; |
fb7d879f OW |
718 | dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); |
719 | /* link is established | |
720 | * start sending messages. | |
721 | */ | |
8120e720 | 722 | mei_hbm_start_req(dev); |
fb7d879f OW |
723 | mutex_unlock(&dev->device_lock); |
724 | return IRQ_HANDLED; | |
725 | } else { | |
726 | dev_dbg(&dev->pdev->dev, "FW not ready.\n"); | |
727 | mutex_unlock(&dev->device_lock); | |
728 | return IRQ_HANDLED; | |
729 | } | |
730 | } | |
5f9092f3 | 731 | /* check slots available for reading */ |
fb7d879f | 732 | slots = mei_count_full_read_slots(dev); |
5fb54fb4 TW |
733 | while (slots > 0) { |
734 | /* we have urgent data to send so break the read */ | |
735 | if (dev->wr_ext_msg.hdr.length) | |
736 | break; | |
737 | dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots); | |
fb7d879f OW |
738 | dev_dbg(&dev->pdev->dev, "call mei_irq_thread_read_handler.\n"); |
739 | rets = mei_irq_thread_read_handler(&complete_list, dev, &slots); | |
740 | if (rets) | |
741 | goto end; | |
742 | } | |
9a84d616 | 743 | rets = mei_irq_thread_write_handler(dev, &complete_list); |
fb7d879f OW |
744 | end: |
745 | dev_dbg(&dev->pdev->dev, "end of bottom half function.\n"); | |
746 | dev->host_hw_state = mei_hcsr_read(dev); | |
726917f0 | 747 | dev->mei_host_buffer_is_empty = mei_hbuf_is_empty(dev); |
fb7d879f OW |
748 | |
749 | bus_message_received = false; | |
750 | if (dev->recvd_msg && waitqueue_active(&dev->wait_recvd_msg)) { | |
751 | dev_dbg(&dev->pdev->dev, "received waiting bus message\n"); | |
752 | bus_message_received = true; | |
753 | } | |
754 | mutex_unlock(&dev->device_lock); | |
755 | if (bus_message_received) { | |
756 | dev_dbg(&dev->pdev->dev, "wake up dev->wait_recvd_msg\n"); | |
757 | wake_up_interruptible(&dev->wait_recvd_msg); | |
758 | bus_message_received = false; | |
759 | } | |
fb601adb | 760 | if (list_empty(&complete_list.list)) |
fb7d879f OW |
761 | return IRQ_HANDLED; |
762 | ||
763 | ||
fb601adb | 764 | list_for_each_entry_safe(cb_pos, cb_next, &complete_list.list, list) { |
db3ed431 | 765 | cl = cb_pos->cl; |
fb601adb | 766 | list_del(&cb_pos->list); |
fb7d879f OW |
767 | if (cl) { |
768 | if (cl != &dev->iamthif_cl) { | |
769 | dev_dbg(&dev->pdev->dev, "completing call back.\n"); | |
770 | _mei_cmpl(cl, cb_pos); | |
771 | cb_pos = NULL; | |
772 | } else if (cl == &dev->iamthif_cl) { | |
19838fb8 | 773 | mei_amthif_complete(dev, cb_pos); |
fb7d879f OW |
774 | } |
775 | } | |
776 | } | |
777 | return IRQ_HANDLED; | |
778 | } |