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