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