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 | ||
40e0b67b | 18 | #include <linux/export.h> |
fb7d879f OW |
19 | #include <linux/pci.h> |
20 | #include <linux/kthread.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/fs.h> | |
23 | #include <linux/jiffies.h> | |
24 | ||
4f3afe1d | 25 | #include <linux/mei.h> |
47a73801 TW |
26 | |
27 | #include "mei_dev.h" | |
0edb23fc | 28 | #include "hbm.h" |
90e0b5f1 | 29 | #include "client.h" |
fb7d879f OW |
30 | |
31 | ||
4c6e22b8 | 32 | /** |
83ce0741 | 33 | * mei_irq_compl_handler - dispatch complete handlers |
4c6e22b8 TW |
34 | * for the completed callbacks |
35 | * | |
36 | * @dev - mei device | |
37 | * @compl_list - list of completed cbs | |
38 | */ | |
39 | void mei_irq_compl_handler(struct mei_device *dev, struct mei_cl_cb *compl_list) | |
40 | { | |
41 | struct mei_cl_cb *cb, *next; | |
42 | struct mei_cl *cl; | |
43 | ||
44 | list_for_each_entry_safe(cb, next, &compl_list->list, list) { | |
45 | cl = cb->cl; | |
46 | list_del(&cb->list); | |
47 | if (!cl) | |
48 | continue; | |
49 | ||
50 | dev_dbg(&dev->pdev->dev, "completing call back.\n"); | |
51 | if (cl == &dev->iamthif_cl) | |
52 | mei_amthif_complete(dev, cb); | |
53 | else | |
db086fa9 | 54 | mei_cl_complete(cl, cb); |
4c6e22b8 TW |
55 | } |
56 | } | |
40e0b67b | 57 | EXPORT_SYMBOL_GPL(mei_irq_compl_handler); |
6e0f180f | 58 | |
fb7d879f | 59 | /** |
6e0f180f | 60 | * mei_cl_hbm_equal - check if hbm is addressed to the client |
fb7d879f | 61 | * |
6e0f180f | 62 | * @cl: host client |
fb7d879f OW |
63 | * @mei_hdr: header of mei client message |
64 | * | |
6e0f180f | 65 | * returns true if matches, false otherwise |
fb7d879f | 66 | */ |
6e0f180f TW |
67 | static inline int mei_cl_hbm_equal(struct mei_cl *cl, |
68 | struct mei_msg_hdr *mei_hdr) | |
fb7d879f | 69 | { |
6e0f180f TW |
70 | return cl->host_client_id == mei_hdr->host_addr && |
71 | cl->me_client_id == mei_hdr->me_addr; | |
72 | } | |
73 | /** | |
74 | * mei_cl_is_reading - checks if the client | |
75 | is the one to read this message | |
76 | * | |
77 | * @cl: mei client | |
78 | * @mei_hdr: header of mei message | |
79 | * | |
80 | * returns true on match and false otherwise | |
81 | */ | |
82 | static bool mei_cl_is_reading(struct mei_cl *cl, struct mei_msg_hdr *mei_hdr) | |
83 | { | |
84 | return mei_cl_hbm_equal(cl, mei_hdr) && | |
fb7d879f | 85 | cl->state == MEI_FILE_CONNECTED && |
6e0f180f | 86 | cl->reading_state != MEI_READ_COMPLETE; |
fb7d879f OW |
87 | } |
88 | ||
89 | /** | |
6e0f180f | 90 | * mei_irq_read_client_message - process client message |
fb7d879f | 91 | * |
fb7d879f OW |
92 | * @dev: the device structure |
93 | * @mei_hdr: header of mei client message | |
6e0f180f | 94 | * @complete_list: An instance of our list structure |
fb7d879f OW |
95 | * |
96 | * returns 0 on success, <0 on failure. | |
97 | */ | |
6e0f180f TW |
98 | static int mei_cl_irq_read_msg(struct mei_device *dev, |
99 | struct mei_msg_hdr *mei_hdr, | |
100 | struct mei_cl_cb *complete_list) | |
fb7d879f OW |
101 | { |
102 | struct mei_cl *cl; | |
6e0f180f | 103 | struct mei_cl_cb *cb, *next; |
479bc59d | 104 | unsigned char *buffer = NULL; |
fb7d879f | 105 | |
6e0f180f TW |
106 | list_for_each_entry_safe(cb, next, &dev->read_list.list, list) { |
107 | cl = cb->cl; | |
108 | if (!cl || !mei_cl_is_reading(cl, mei_hdr)) | |
109 | continue; | |
110 | ||
111 | cl->reading_state = MEI_READING; | |
112 | ||
113 | if (cb->response_buffer.size == 0 || | |
114 | cb->response_buffer.data == NULL) { | |
c0abffbd | 115 | cl_err(dev, cl, "response buffer is not allocated.\n"); |
6e0f180f TW |
116 | list_del(&cb->list); |
117 | return -ENOMEM; | |
118 | } | |
119 | ||
120 | if (cb->response_buffer.size < mei_hdr->length + cb->buf_idx) { | |
c0abffbd | 121 | cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", |
fcb136e1 TW |
122 | cb->response_buffer.size, |
123 | mei_hdr->length, cb->buf_idx); | |
46e407dd WY |
124 | buffer = krealloc(cb->response_buffer.data, |
125 | mei_hdr->length + cb->buf_idx, | |
126 | GFP_KERNEL); | |
fcb136e1 | 127 | |
46e407dd | 128 | if (!buffer) { |
c0abffbd | 129 | cl_err(dev, cl, "allocation failed.\n"); |
fcb136e1 TW |
130 | list_del(&cb->list); |
131 | return -ENOMEM; | |
132 | } | |
46e407dd | 133 | cb->response_buffer.data = buffer; |
fcb136e1 TW |
134 | cb->response_buffer.size = |
135 | mei_hdr->length + cb->buf_idx; | |
fb7d879f OW |
136 | } |
137 | ||
6e0f180f TW |
138 | buffer = cb->response_buffer.data + cb->buf_idx; |
139 | mei_read_slots(dev, buffer, mei_hdr->length); | |
140 | ||
141 | cb->buf_idx += mei_hdr->length; | |
142 | if (mei_hdr->msg_complete) { | |
143 | cl->status = 0; | |
144 | list_del(&cb->list); | |
c0abffbd | 145 | cl_dbg(dev, cl, "completed read length = %lu\n", |
6e0f180f TW |
146 | cb->buf_idx); |
147 | list_add_tail(&cb->list, &complete_list->list); | |
148 | } | |
149 | break; | |
fb7d879f OW |
150 | } |
151 | ||
fb7d879f OW |
152 | dev_dbg(&dev->pdev->dev, "message read\n"); |
153 | if (!buffer) { | |
edf1eed4 | 154 | mei_read_slots(dev, dev->rd_msg_buf, mei_hdr->length); |
15d4acc5 TW |
155 | dev_dbg(&dev->pdev->dev, "discarding message " MEI_HDR_FMT "\n", |
156 | MEI_HDR_PRM(mei_hdr)); | |
fb7d879f OW |
157 | } |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
6bb948c9 TW |
162 | /** |
163 | * mei_cl_irq_disconnect_rsp - send disconnection response message | |
164 | * | |
165 | * @cl: client | |
166 | * @cb: callback block. | |
6bb948c9 TW |
167 | * @cmpl_list: complete list. |
168 | * | |
169 | * returns 0, OK; otherwise, error. | |
170 | */ | |
171 | static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, | |
9d098192 | 172 | struct mei_cl_cb *cmpl_list) |
6bb948c9 TW |
173 | { |
174 | struct mei_device *dev = cl->dev; | |
9d098192 TW |
175 | u32 msg_slots; |
176 | int slots; | |
6bb948c9 TW |
177 | int ret; |
178 | ||
9d098192 TW |
179 | slots = mei_hbuf_empty_slots(dev); |
180 | msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response)); | |
6bb948c9 | 181 | |
9d098192 | 182 | if (slots < msg_slots) |
6bb948c9 TW |
183 | return -EMSGSIZE; |
184 | ||
6bb948c9 TW |
185 | ret = mei_hbm_cl_disconnect_rsp(dev, cl); |
186 | ||
187 | cl->state = MEI_FILE_DISCONNECTED; | |
188 | cl->status = 0; | |
31a5ef24 | 189 | list_del(&cb->list); |
6bb948c9 TW |
190 | mei_io_cb_free(cb); |
191 | ||
192 | return ret; | |
193 | } | |
194 | ||
195 | ||
196 | ||
fb7d879f | 197 | /** |
6220d6a0 TW |
198 | * mei_cl_irq_close - processes close related operation from |
199 | * interrupt thread context - send disconnect request | |
fb7d879f | 200 | * |
6220d6a0 TW |
201 | * @cl: client |
202 | * @cb: callback block. | |
fb7d879f OW |
203 | * @cmpl_list: complete list. |
204 | * | |
205 | * returns 0, OK; otherwise, error. | |
206 | */ | |
6220d6a0 | 207 | static int mei_cl_irq_close(struct mei_cl *cl, struct mei_cl_cb *cb, |
9d098192 | 208 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 209 | { |
6220d6a0 | 210 | struct mei_device *dev = cl->dev; |
9d098192 TW |
211 | u32 msg_slots; |
212 | int slots; | |
6220d6a0 | 213 | |
9d098192 TW |
214 | msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); |
215 | slots = mei_hbuf_empty_slots(dev); | |
fb7d879f | 216 | |
9d098192 | 217 | if (slots < msg_slots) |
c8c8d080 TW |
218 | return -EMSGSIZE; |
219 | ||
8120e720 | 220 | if (mei_hbm_cl_disconnect_req(dev, cl)) { |
b45f3ccf | 221 | cl->status = 0; |
6220d6a0 TW |
222 | cb->buf_idx = 0; |
223 | list_move_tail(&cb->list, &cmpl_list->list); | |
c8c8d080 | 224 | return -EIO; |
fb7d879f OW |
225 | } |
226 | ||
c8c8d080 TW |
227 | cl->state = MEI_FILE_DISCONNECTING; |
228 | cl->status = 0; | |
6220d6a0 TW |
229 | cb->buf_idx = 0; |
230 | list_move_tail(&cb->list, &dev->ctrl_rd_list.list); | |
c8c8d080 TW |
231 | cl->timer_count = MEI_CONNECT_TIMEOUT; |
232 | ||
fb7d879f OW |
233 | return 0; |
234 | } | |
235 | ||
fb7d879f OW |
236 | |
237 | /** | |
6220d6a0 TW |
238 | * mei_cl_irq_close - processes client read related operation from the |
239 | * interrupt thread context - request for flow control credits | |
fb7d879f | 240 | * |
6220d6a0 TW |
241 | * @cl: client |
242 | * @cb: callback block. | |
fb7d879f OW |
243 | * @cmpl_list: complete list. |
244 | * | |
245 | * returns 0, OK; otherwise, error. | |
246 | */ | |
6220d6a0 | 247 | static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, |
9d098192 | 248 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 249 | { |
6220d6a0 | 250 | struct mei_device *dev = cl->dev; |
9d098192 TW |
251 | u32 msg_slots; |
252 | int slots; | |
2ebf8c94 TW |
253 | int ret; |
254 | ||
9d098192 TW |
255 | msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); |
256 | slots = mei_hbuf_empty_slots(dev); | |
2ebf8c94 | 257 | |
9d098192 | 258 | if (slots < msg_slots) |
c8c8d080 | 259 | return -EMSGSIZE; |
7bdf72d3 | 260 | |
2ebf8c94 TW |
261 | ret = mei_hbm_cl_flow_control_req(dev, cl); |
262 | if (ret) { | |
263 | cl->status = ret; | |
6220d6a0 TW |
264 | cb->buf_idx = 0; |
265 | list_move_tail(&cb->list, &cmpl_list->list); | |
2ebf8c94 | 266 | return ret; |
1ccb7b62 | 267 | } |
2ebf8c94 | 268 | |
6220d6a0 | 269 | list_move_tail(&cb->list, &dev->read_list.list); |
1ccb7b62 | 270 | |
fb7d879f OW |
271 | return 0; |
272 | } | |
273 | ||
274 | ||
275 | /** | |
02a7eecc | 276 | * mei_cl_irq_connect - send connect request in irq_thread context |
fb7d879f | 277 | * |
6220d6a0 TW |
278 | * @cl: client |
279 | * @cb: callback block. | |
fb7d879f OW |
280 | * @cmpl_list: complete list. |
281 | * | |
282 | * returns 0, OK; otherwise, error. | |
283 | */ | |
02a7eecc | 284 | static int mei_cl_irq_connect(struct mei_cl *cl, struct mei_cl_cb *cb, |
9d098192 | 285 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 286 | { |
6220d6a0 | 287 | struct mei_device *dev = cl->dev; |
9d098192 TW |
288 | u32 msg_slots; |
289 | int slots; | |
2ebf8c94 | 290 | int ret; |
6220d6a0 | 291 | |
9d098192 TW |
292 | msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); |
293 | slots = mei_hbuf_empty_slots(dev); | |
c8c8d080 | 294 | |
02a7eecc TW |
295 | if (mei_cl_is_other_connecting(cl)) |
296 | return 0; | |
297 | ||
9d098192 | 298 | if (slots < msg_slots) |
c8c8d080 | 299 | return -EMSGSIZE; |
c8c8d080 | 300 | |
b45f3ccf | 301 | cl->state = MEI_FILE_CONNECTING; |
c8c8d080 | 302 | |
2ebf8c94 TW |
303 | ret = mei_hbm_cl_connect_req(dev, cl); |
304 | if (ret) { | |
305 | cl->status = ret; | |
6220d6a0 TW |
306 | cb->buf_idx = 0; |
307 | list_del(&cb->list); | |
2ebf8c94 | 308 | return ret; |
b45f3ccf | 309 | } |
6220d6a0 TW |
310 | |
311 | list_move_tail(&cb->list, &dev->ctrl_rd_list.list); | |
312 | cl->timer_count = MEI_CONNECT_TIMEOUT; | |
fb7d879f OW |
313 | return 0; |
314 | } | |
315 | ||
fb7d879f | 316 | |
fb7d879f | 317 | /** |
393b148f | 318 | * mei_irq_read_handler - bottom half read routine after ISR to |
fb7d879f OW |
319 | * handle the read processing. |
320 | * | |
fb7d879f | 321 | * @dev: the device structure |
06ecd645 | 322 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
323 | * @slots: slots to read. |
324 | * | |
325 | * returns 0 on success, <0 on failure. | |
326 | */ | |
06ecd645 TW |
327 | int mei_irq_read_handler(struct mei_device *dev, |
328 | struct mei_cl_cb *cmpl_list, s32 *slots) | |
fb7d879f OW |
329 | { |
330 | struct mei_msg_hdr *mei_hdr; | |
10ee9074 TW |
331 | struct mei_cl *cl; |
332 | int ret; | |
fb7d879f OW |
333 | |
334 | if (!dev->rd_msg_hdr) { | |
827eef51 | 335 | dev->rd_msg_hdr = mei_read_hdr(dev); |
fb7d879f OW |
336 | (*slots)--; |
337 | dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots); | |
338 | } | |
339 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | |
15d4acc5 | 340 | dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
fb7d879f OW |
341 | |
342 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | |
10ee9074 TW |
343 | dev_err(&dev->pdev->dev, "corrupted message header 0x%08X\n", |
344 | dev->rd_msg_hdr); | |
fb7d879f OW |
345 | ret = -EBADMSG; |
346 | goto end; | |
347 | } | |
348 | ||
10ee9074 TW |
349 | if (mei_slots2data(*slots) < mei_hdr->length) { |
350 | dev_err(&dev->pdev->dev, "less data available than length=%08x.\n", | |
fb7d879f OW |
351 | *slots); |
352 | /* we can't read the message */ | |
b1b94b5d | 353 | ret = -ENODATA; |
fb7d879f OW |
354 | goto end; |
355 | } | |
356 | ||
10ee9074 TW |
357 | /* HBM message */ |
358 | if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { | |
544f9460 TW |
359 | ret = mei_hbm_dispatch(dev, mei_hdr); |
360 | if (ret) { | |
361 | dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch failed ret = %d\n", | |
362 | ret); | |
363 | goto end; | |
364 | } | |
10ee9074 TW |
365 | goto reset_slots; |
366 | } | |
15d4acc5 | 367 | |
83ce0741 | 368 | /* find recipient cl */ |
10ee9074 TW |
369 | list_for_each_entry(cl, &dev->file_list, link) { |
370 | if (mei_cl_hbm_equal(cl, mei_hdr)) { | |
371 | cl_dbg(dev, cl, "got a message\n"); | |
372 | break; | |
373 | } | |
374 | } | |
375 | ||
83ce0741 | 376 | /* if no recipient cl was found we assume corrupted header */ |
10ee9074 TW |
377 | if (&cl->link == &dev->file_list) { |
378 | dev_err(&dev->pdev->dev, "no destination client found 0x%08X\n", | |
379 | dev->rd_msg_hdr); | |
380 | ret = -EBADMSG; | |
381 | goto end; | |
382 | } | |
383 | ||
384 | if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id && | |
385 | MEI_FILE_CONNECTED == dev->iamthif_cl.state && | |
386 | dev->iamthif_state == MEI_IAMTHIF_READING) { | |
19838fb8 | 387 | |
5ceb46e2 | 388 | ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list); |
10ee9074 TW |
389 | if (ret) { |
390 | dev_err(&dev->pdev->dev, "mei_amthif_irq_read_msg failed = %d\n", | |
391 | ret); | |
fb7d879f | 392 | goto end; |
10ee9074 | 393 | } |
fb7d879f | 394 | } else { |
6e0f180f | 395 | ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list); |
10ee9074 TW |
396 | if (ret) { |
397 | dev_err(&dev->pdev->dev, "mei_cl_irq_read_msg failed = %d\n", | |
398 | ret); | |
fb7d879f | 399 | goto end; |
10ee9074 | 400 | } |
fb7d879f OW |
401 | } |
402 | ||
10ee9074 | 403 | reset_slots: |
fb7d879f OW |
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 */ | |
6e0f180f | 410 | dev_err(&dev->pdev->dev, "resetting due to slots overflow.\n"); |
fb7d879f OW |
411 | /* set the event since message has been read */ |
412 | ret = -ERANGE; | |
413 | goto end; | |
414 | } | |
415 | end: | |
416 | return ret; | |
417 | } | |
40e0b67b | 418 | EXPORT_SYMBOL_GPL(mei_irq_read_handler); |
fb7d879f OW |
419 | |
420 | ||
421 | /** | |
06ecd645 TW |
422 | * mei_irq_write_handler - dispatch write requests |
423 | * after irq received | |
fb7d879f | 424 | * |
fb7d879f | 425 | * @dev: the device structure |
9a84d616 | 426 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
427 | * |
428 | * returns 0 on success, <0 on failure. | |
429 | */ | |
c8c8d080 | 430 | int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) |
fb7d879f OW |
431 | { |
432 | ||
433 | struct mei_cl *cl; | |
6220d6a0 | 434 | struct mei_cl_cb *cb, *next; |
fb601adb | 435 | struct mei_cl_cb *list; |
9a84d616 | 436 | s32 slots; |
fb7d879f OW |
437 | int ret; |
438 | ||
6aae48ff TW |
439 | |
440 | if (!mei_hbuf_acquire(dev)) | |
fb7d879f | 441 | return 0; |
6aae48ff | 442 | |
9a84d616 TW |
443 | slots = mei_hbuf_empty_slots(dev); |
444 | if (slots <= 0) | |
7d5e0e59 TW |
445 | return -EMSGSIZE; |
446 | ||
fb7d879f OW |
447 | /* complete all waiting for write CB */ |
448 | dev_dbg(&dev->pdev->dev, "complete all waiting for write cb.\n"); | |
449 | ||
450 | list = &dev->write_waiting_list; | |
6220d6a0 TW |
451 | list_for_each_entry_safe(cb, next, &list->list, list) { |
452 | cl = cb->cl; | |
b7cd2d9f TW |
453 | if (cl == NULL) |
454 | continue; | |
455 | ||
456 | cl->status = 0; | |
6220d6a0 | 457 | list_del(&cb->list); |
34ec4366 | 458 | if (cb->fop_type == MEI_FOP_WRITE && |
4b8960b4 | 459 | cl != &dev->iamthif_cl) { |
c0abffbd | 460 | cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); |
b7cd2d9f | 461 | cl->writing_state = MEI_WRITE_COMPLETE; |
6220d6a0 | 462 | list_add_tail(&cb->list, &cmpl_list->list); |
b7cd2d9f TW |
463 | } |
464 | if (cl == &dev->iamthif_cl) { | |
c0abffbd | 465 | cl_dbg(dev, cl, "check iamthif flow control.\n"); |
b7cd2d9f | 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; | |
5877255d | 476 | wake_up(&dev->wait_stop_wd); |
fb7d879f OW |
477 | } |
478 | ||
64092858 | 479 | if (mei_cl_is_connected(&dev->wd_cl)) { |
fb7d879f | 480 | if (dev->wd_pending && |
90e0b5f1 | 481 | mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { |
b6d81fd6 TW |
482 | ret = mei_wd_send(dev); |
483 | if (ret) | |
484 | return ret; | |
eb9af0ac | 485 | dev->wd_pending = false; |
fb7d879f OW |
486 | } |
487 | } | |
fb7d879f OW |
488 | |
489 | /* complete control write list CB */ | |
c8372094 | 490 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); |
6220d6a0 TW |
491 | list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { |
492 | cl = cb->cl; | |
c8372094 | 493 | if (!cl) { |
6220d6a0 | 494 | list_del(&cb->list); |
c8372094 TW |
495 | return -ENODEV; |
496 | } | |
6220d6a0 | 497 | switch (cb->fop_type) { |
4b8960b4 | 498 | case MEI_FOP_CLOSE: |
c8372094 | 499 | /* send disconnect message */ |
9d098192 | 500 | ret = mei_cl_irq_close(cl, cb, cmpl_list); |
c8372094 TW |
501 | if (ret) |
502 | return ret; | |
fb7d879f | 503 | |
c8372094 | 504 | break; |
4b8960b4 | 505 | case MEI_FOP_READ: |
c8372094 | 506 | /* send flow control message */ |
9d098192 | 507 | ret = mei_cl_irq_read(cl, cb, cmpl_list); |
c8372094 TW |
508 | if (ret) |
509 | return ret; | |
fb7d879f | 510 | |
c8372094 | 511 | break; |
02a7eecc | 512 | case MEI_FOP_CONNECT: |
c8372094 | 513 | /* connect message */ |
9d098192 | 514 | ret = mei_cl_irq_connect(cl, cb, cmpl_list); |
c8372094 TW |
515 | if (ret) |
516 | return ret; | |
fb7d879f | 517 | |
c8372094 | 518 | break; |
6bb948c9 TW |
519 | case MEI_FOP_DISCONNECT_RSP: |
520 | /* send disconnect resp */ | |
9d098192 | 521 | ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list); |
6bb948c9 TW |
522 | if (ret) |
523 | return ret; | |
31a5ef24 | 524 | break; |
c8372094 TW |
525 | default: |
526 | BUG(); | |
fb7d879f | 527 | } |
c8372094 | 528 | |
fb7d879f OW |
529 | } |
530 | /* complete write list CB */ | |
b7cd2d9f | 531 | dev_dbg(&dev->pdev->dev, "complete write list cb.\n"); |
6220d6a0 TW |
532 | list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { |
533 | cl = cb->cl; | |
b7cd2d9f TW |
534 | if (cl == NULL) |
535 | continue; | |
be9d87a7 | 536 | if (cl == &dev->iamthif_cl) |
9d098192 | 537 | ret = mei_amthif_irq_write(cl, cb, cmpl_list); |
be9d87a7 | 538 | else |
9d098192 | 539 | ret = mei_cl_irq_write(cl, cb, cmpl_list); |
be9d87a7 TW |
540 | if (ret) |
541 | return ret; | |
fb7d879f OW |
542 | } |
543 | return 0; | |
544 | } | |
40e0b67b | 545 | EXPORT_SYMBOL_GPL(mei_irq_write_handler); |
fb7d879f OW |
546 | |
547 | ||
548 | ||
549 | /** | |
550 | * mei_timer - timer function. | |
551 | * | |
552 | * @work: pointer to the work_struct structure | |
553 | * | |
fb7d879f | 554 | */ |
a61c6530 | 555 | void mei_timer(struct work_struct *work) |
fb7d879f OW |
556 | { |
557 | unsigned long timeout; | |
31f88f57 | 558 | struct mei_cl *cl; |
fb7d879f OW |
559 | struct mei_cl_cb *cb_pos = NULL; |
560 | struct mei_cl_cb *cb_next = NULL; | |
561 | ||
562 | struct mei_device *dev = container_of(work, | |
a61c6530 | 563 | struct mei_device, timer_work.work); |
fb7d879f OW |
564 | |
565 | ||
566 | mutex_lock(&dev->device_lock); | |
66ae460b TW |
567 | |
568 | /* Catch interrupt stalls during HBM init handshake */ | |
569 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && | |
570 | dev->hbm_state != MEI_HBM_IDLE) { | |
571 | ||
572 | if (dev->init_clients_timer) { | |
573 | if (--dev->init_clients_timer == 0) { | |
574 | dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n", | |
575 | dev->hbm_state); | |
33ec0826 | 576 | mei_reset(dev); |
66ae460b | 577 | goto out; |
fb7d879f OW |
578 | } |
579 | } | |
fb7d879f | 580 | } |
66ae460b TW |
581 | |
582 | if (dev->dev_state != MEI_DEV_ENABLED) | |
583 | goto out; | |
584 | ||
fb7d879f | 585 | /*** connect/disconnect timeouts ***/ |
31f88f57 TW |
586 | list_for_each_entry(cl, &dev->file_list, link) { |
587 | if (cl->timer_count) { | |
588 | if (--cl->timer_count == 0) { | |
33ec0826 TW |
589 | dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n"); |
590 | mei_reset(dev); | |
fb7d879f OW |
591 | goto out; |
592 | } | |
593 | } | |
594 | } | |
595 | ||
64092858 TW |
596 | if (!mei_cl_is_connected(&dev->iamthif_cl)) |
597 | goto out; | |
598 | ||
fb7d879f OW |
599 | if (dev->iamthif_stall_timer) { |
600 | if (--dev->iamthif_stall_timer == 0) { | |
33ec0826 TW |
601 | dev_err(&dev->pdev->dev, "timer: amthif hanged.\n"); |
602 | mei_reset(dev); | |
fb7d879f OW |
603 | dev->iamthif_msg_buf_size = 0; |
604 | dev->iamthif_msg_buf_index = 0; | |
eb9af0ac TW |
605 | dev->iamthif_canceled = false; |
606 | dev->iamthif_ioctl = true; | |
fb7d879f OW |
607 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
608 | dev->iamthif_timer = 0; | |
609 | ||
601a1efa TW |
610 | mei_io_cb_free(dev->iamthif_current_cb); |
611 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
612 | |
613 | dev->iamthif_file_object = NULL; | |
19838fb8 | 614 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
615 | } |
616 | } | |
617 | ||
618 | if (dev->iamthif_timer) { | |
619 | ||
620 | timeout = dev->iamthif_timer + | |
3870c320 | 621 | mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); |
fb7d879f OW |
622 | |
623 | dev_dbg(&dev->pdev->dev, "dev->iamthif_timer = %ld\n", | |
624 | dev->iamthif_timer); | |
625 | dev_dbg(&dev->pdev->dev, "timeout = %ld\n", timeout); | |
626 | dev_dbg(&dev->pdev->dev, "jiffies = %ld\n", jiffies); | |
627 | if (time_after(jiffies, timeout)) { | |
628 | /* | |
629 | * User didn't read the AMTHI data on time (15sec) | |
630 | * freeing AMTHI for other requests | |
631 | */ | |
632 | ||
633 | dev_dbg(&dev->pdev->dev, "freeing AMTHI for other requests\n"); | |
634 | ||
e773efc4 TW |
635 | list_for_each_entry_safe(cb_pos, cb_next, |
636 | &dev->amthif_rd_complete_list.list, list) { | |
fb7d879f | 637 | |
31f88f57 | 638 | cl = cb_pos->file_object->private_data; |
fb7d879f | 639 | |
b7cd2d9f | 640 | /* Finding the AMTHI entry. */ |
31f88f57 | 641 | if (cl == &dev->iamthif_cl) |
fb601adb | 642 | list_del(&cb_pos->list); |
fb7d879f | 643 | } |
601a1efa TW |
644 | mei_io_cb_free(dev->iamthif_current_cb); |
645 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
646 | |
647 | dev->iamthif_file_object->private_data = NULL; | |
648 | dev->iamthif_file_object = NULL; | |
fb7d879f | 649 | dev->iamthif_timer = 0; |
19838fb8 | 650 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
651 | |
652 | } | |
653 | } | |
654 | out: | |
33ec0826 TW |
655 | if (dev->dev_state != MEI_DEV_DISABLED) |
656 | schedule_delayed_work(&dev->timer_work, 2 * HZ); | |
441ab50f | 657 | mutex_unlock(&dev->device_lock); |
fb7d879f OW |
658 | } |
659 |