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