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; | |
c54bf3ab | 46 | list_del_init(&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 | { |
1df629ef | 68 | return mei_cl_host_addr(cl) == mei_hdr->host_addr && |
d49ed64a | 69 | mei_cl_me_id(cl) == mei_hdr->me_addr; |
6e0f180f | 70 | } |
fb7d879f | 71 | |
331e4187 TW |
72 | /** |
73 | * mei_irq_discard_msg - discard received message | |
74 | * | |
75 | * @dev: mei device | |
76 | * @hdr: message header | |
77 | */ | |
78 | static inline | |
79 | void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) | |
80 | { | |
81 | /* | |
82 | * no need to check for size as it is guarantied | |
83 | * that length fits into rd_msg_buf | |
84 | */ | |
85 | mei_read_slots(dev, dev->rd_msg_buf, hdr->length); | |
86 | dev_dbg(dev->dev, "discarding message " MEI_HDR_FMT "\n", | |
87 | MEI_HDR_PRM(hdr)); | |
88 | } | |
89 | ||
fb7d879f | 90 | /** |
ce23139c | 91 | * mei_cl_irq_read_msg - process client message |
fb7d879f | 92 | * |
3d33ff24 | 93 | * @cl: reading client |
fb7d879f | 94 | * @mei_hdr: header of mei client message |
3d33ff24 | 95 | * @complete_list: completion list |
fb7d879f | 96 | * |
3d33ff24 | 97 | * Return: always 0 |
fb7d879f | 98 | */ |
331e4187 TW |
99 | int mei_cl_irq_read_msg(struct mei_cl *cl, |
100 | struct mei_msg_hdr *mei_hdr, | |
101 | struct mei_cl_cb *complete_list) | |
fb7d879f | 102 | { |
3d33ff24 TW |
103 | struct mei_device *dev = cl->dev; |
104 | struct mei_cl_cb *cb; | |
479bc59d | 105 | unsigned char *buffer = NULL; |
fb7d879f | 106 | |
a9bed610 TW |
107 | cb = list_first_entry_or_null(&cl->rd_pending, struct mei_cl_cb, list); |
108 | if (!cb) { | |
109 | cl_err(dev, cl, "pending read cb not found\n"); | |
3d33ff24 TW |
110 | goto out; |
111 | } | |
6e0f180f | 112 | |
f3de9b63 | 113 | if (!mei_cl_is_connected(cl)) { |
a9bed610 TW |
114 | cl_dbg(dev, cl, "not connected\n"); |
115 | cb->status = -ENODEV; | |
3d33ff24 TW |
116 | goto out; |
117 | } | |
6e0f180f | 118 | |
5db7514d | 119 | if (cb->buf.size == 0 || cb->buf.data == NULL) { |
3d33ff24 TW |
120 | cl_err(dev, cl, "response buffer is not allocated.\n"); |
121 | list_move_tail(&cb->list, &complete_list->list); | |
122 | cb->status = -ENOMEM; | |
123 | goto out; | |
124 | } | |
6e0f180f | 125 | |
5db7514d | 126 | if (cb->buf.size < mei_hdr->length + cb->buf_idx) { |
3d33ff24 | 127 | cl_dbg(dev, cl, "message overflow. size %d len %d idx %ld\n", |
5db7514d TW |
128 | cb->buf.size, mei_hdr->length, cb->buf_idx); |
129 | buffer = krealloc(cb->buf.data, mei_hdr->length + cb->buf_idx, | |
3d33ff24 TW |
130 | GFP_KERNEL); |
131 | ||
132 | if (!buffer) { | |
133 | cb->status = -ENOMEM; | |
134 | list_move_tail(&cb->list, &complete_list->list); | |
135 | goto out; | |
6e0f180f | 136 | } |
5db7514d TW |
137 | cb->buf.data = buffer; |
138 | cb->buf.size = mei_hdr->length + cb->buf_idx; | |
fb7d879f OW |
139 | } |
140 | ||
5db7514d | 141 | buffer = cb->buf.data + cb->buf_idx; |
3d33ff24 TW |
142 | mei_read_slots(dev, buffer, mei_hdr->length); |
143 | ||
144 | cb->buf_idx += mei_hdr->length; | |
331e4187 | 145 | |
3d33ff24 | 146 | if (mei_hdr->msg_complete) { |
331e4187 | 147 | cb->read_time = jiffies; |
a9bed610 | 148 | cl_dbg(dev, cl, "completed read length = %lu\n", cb->buf_idx); |
3d33ff24 TW |
149 | list_move_tail(&cb->list, &complete_list->list); |
150 | } | |
151 | ||
152 | out: | |
331e4187 TW |
153 | if (!buffer) |
154 | mei_irq_discard_msg(dev, mei_hdr); | |
fb7d879f OW |
155 | |
156 | return 0; | |
157 | } | |
158 | ||
6bb948c9 TW |
159 | /** |
160 | * mei_cl_irq_disconnect_rsp - send disconnection response message | |
161 | * | |
162 | * @cl: client | |
163 | * @cb: callback block. | |
6bb948c9 TW |
164 | * @cmpl_list: complete list. |
165 | * | |
a8605ea2 | 166 | * Return: 0, OK; otherwise, error. |
6bb948c9 TW |
167 | */ |
168 | static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, | |
9d098192 | 169 | struct mei_cl_cb *cmpl_list) |
6bb948c9 TW |
170 | { |
171 | struct mei_device *dev = cl->dev; | |
9d098192 TW |
172 | u32 msg_slots; |
173 | int slots; | |
6bb948c9 TW |
174 | int ret; |
175 | ||
9d098192 TW |
176 | slots = mei_hbuf_empty_slots(dev); |
177 | msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_response)); | |
6bb948c9 | 178 | |
9d098192 | 179 | if (slots < msg_slots) |
6bb948c9 TW |
180 | return -EMSGSIZE; |
181 | ||
6bb948c9 | 182 | ret = mei_hbm_cl_disconnect_rsp(dev, cl); |
3c666182 | 183 | mei_cl_set_disconnected(cl); |
6bb948c9 | 184 | mei_io_cb_free(cb); |
d49ed64a AU |
185 | mei_me_cl_put(cl->me_cl); |
186 | cl->me_cl = NULL; | |
6bb948c9 TW |
187 | |
188 | return ret; | |
189 | } | |
190 | ||
fb7d879f | 191 | /** |
ce23139c | 192 | * mei_cl_irq_read - processes client read related operation from the |
6220d6a0 | 193 | * interrupt thread context - request for flow control credits |
fb7d879f | 194 | * |
6220d6a0 TW |
195 | * @cl: client |
196 | * @cb: callback block. | |
fb7d879f OW |
197 | * @cmpl_list: complete list. |
198 | * | |
a8605ea2 | 199 | * Return: 0, OK; otherwise, error. |
fb7d879f | 200 | */ |
6220d6a0 | 201 | static int mei_cl_irq_read(struct mei_cl *cl, struct mei_cl_cb *cb, |
9d098192 | 202 | struct mei_cl_cb *cmpl_list) |
fb7d879f | 203 | { |
6220d6a0 | 204 | struct mei_device *dev = cl->dev; |
9d098192 TW |
205 | u32 msg_slots; |
206 | int slots; | |
2ebf8c94 TW |
207 | int ret; |
208 | ||
9d098192 TW |
209 | msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); |
210 | slots = mei_hbuf_empty_slots(dev); | |
2ebf8c94 | 211 | |
9d098192 | 212 | if (slots < msg_slots) |
c8c8d080 | 213 | return -EMSGSIZE; |
7bdf72d3 | 214 | |
2ebf8c94 TW |
215 | ret = mei_hbm_cl_flow_control_req(dev, cl); |
216 | if (ret) { | |
217 | cl->status = ret; | |
6220d6a0 TW |
218 | cb->buf_idx = 0; |
219 | list_move_tail(&cb->list, &cmpl_list->list); | |
2ebf8c94 | 220 | return ret; |
1ccb7b62 | 221 | } |
2ebf8c94 | 222 | |
a9bed610 | 223 | list_move_tail(&cb->list, &cl->rd_pending); |
1ccb7b62 | 224 | |
fb7d879f OW |
225 | return 0; |
226 | } | |
227 | ||
fb7d879f | 228 | /** |
393b148f | 229 | * mei_irq_read_handler - bottom half read routine after ISR to |
fb7d879f OW |
230 | * handle the read processing. |
231 | * | |
fb7d879f | 232 | * @dev: the device structure |
06ecd645 | 233 | * @cmpl_list: An instance of our list structure |
fb7d879f OW |
234 | * @slots: slots to read. |
235 | * | |
a8605ea2 | 236 | * Return: 0 on success, <0 on failure. |
fb7d879f | 237 | */ |
06ecd645 TW |
238 | int mei_irq_read_handler(struct mei_device *dev, |
239 | struct mei_cl_cb *cmpl_list, s32 *slots) | |
fb7d879f OW |
240 | { |
241 | struct mei_msg_hdr *mei_hdr; | |
10ee9074 TW |
242 | struct mei_cl *cl; |
243 | int ret; | |
fb7d879f OW |
244 | |
245 | if (!dev->rd_msg_hdr) { | |
827eef51 | 246 | dev->rd_msg_hdr = mei_read_hdr(dev); |
fb7d879f | 247 | (*slots)--; |
2bf94cab | 248 | dev_dbg(dev->dev, "slots =%08x.\n", *slots); |
fb7d879f OW |
249 | } |
250 | mei_hdr = (struct mei_msg_hdr *) &dev->rd_msg_hdr; | |
2bf94cab | 251 | dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr)); |
fb7d879f OW |
252 | |
253 | if (mei_hdr->reserved || !dev->rd_msg_hdr) { | |
2bf94cab | 254 | dev_err(dev->dev, "corrupted message header 0x%08X\n", |
10ee9074 | 255 | dev->rd_msg_hdr); |
fb7d879f OW |
256 | ret = -EBADMSG; |
257 | goto end; | |
258 | } | |
259 | ||
10ee9074 | 260 | if (mei_slots2data(*slots) < mei_hdr->length) { |
2bf94cab | 261 | dev_err(dev->dev, "less data available than length=%08x.\n", |
fb7d879f OW |
262 | *slots); |
263 | /* we can't read the message */ | |
b1b94b5d | 264 | ret = -ENODATA; |
fb7d879f OW |
265 | goto end; |
266 | } | |
267 | ||
10ee9074 TW |
268 | /* HBM message */ |
269 | if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) { | |
544f9460 TW |
270 | ret = mei_hbm_dispatch(dev, mei_hdr); |
271 | if (ret) { | |
2bf94cab | 272 | dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n", |
544f9460 TW |
273 | ret); |
274 | goto end; | |
275 | } | |
10ee9074 TW |
276 | goto reset_slots; |
277 | } | |
15d4acc5 | 278 | |
83ce0741 | 279 | /* find recipient cl */ |
10ee9074 TW |
280 | list_for_each_entry(cl, &dev->file_list, link) { |
281 | if (mei_cl_hbm_equal(cl, mei_hdr)) { | |
282 | cl_dbg(dev, cl, "got a message\n"); | |
283 | break; | |
284 | } | |
285 | } | |
286 | ||
83ce0741 | 287 | /* if no recipient cl was found we assume corrupted header */ |
10ee9074 | 288 | if (&cl->link == &dev->file_list) { |
2bf94cab | 289 | dev_err(dev->dev, "no destination client found 0x%08X\n", |
10ee9074 TW |
290 | dev->rd_msg_hdr); |
291 | ret = -EBADMSG; | |
292 | goto end; | |
293 | } | |
294 | ||
db4756fd TW |
295 | if (cl == &dev->iamthif_cl) { |
296 | ret = mei_amthif_irq_read_msg(cl, mei_hdr, cmpl_list); | |
fb7d879f | 297 | } else { |
3d33ff24 | 298 | ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); |
fb7d879f OW |
299 | } |
300 | ||
3d33ff24 | 301 | |
10ee9074 | 302 | reset_slots: |
fb7d879f OW |
303 | /* reset the number of slots and header */ |
304 | *slots = mei_count_full_read_slots(dev); | |
305 | dev->rd_msg_hdr = 0; | |
306 | ||
307 | if (*slots == -EOVERFLOW) { | |
308 | /* overflow - reset */ | |
2bf94cab | 309 | dev_err(dev->dev, "resetting due to slots overflow.\n"); |
fb7d879f OW |
310 | /* set the event since message has been read */ |
311 | ret = -ERANGE; | |
312 | goto end; | |
313 | } | |
314 | end: | |
315 | return ret; | |
316 | } | |
40e0b67b | 317 | EXPORT_SYMBOL_GPL(mei_irq_read_handler); |
fb7d879f OW |
318 | |
319 | ||
320 | /** | |
06ecd645 TW |
321 | * mei_irq_write_handler - dispatch write requests |
322 | * after irq received | |
fb7d879f | 323 | * |
fb7d879f | 324 | * @dev: the device structure |
9a84d616 | 325 | * @cmpl_list: An instance of our list structure |
fb7d879f | 326 | * |
a8605ea2 | 327 | * Return: 0 on success, <0 on failure. |
fb7d879f | 328 | */ |
c8c8d080 | 329 | int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) |
fb7d879f OW |
330 | { |
331 | ||
332 | struct mei_cl *cl; | |
6220d6a0 | 333 | struct mei_cl_cb *cb, *next; |
fb601adb | 334 | struct mei_cl_cb *list; |
9a84d616 | 335 | s32 slots; |
fb7d879f OW |
336 | int ret; |
337 | ||
6aae48ff TW |
338 | |
339 | if (!mei_hbuf_acquire(dev)) | |
fb7d879f | 340 | return 0; |
6aae48ff | 341 | |
9a84d616 TW |
342 | slots = mei_hbuf_empty_slots(dev); |
343 | if (slots <= 0) | |
7d5e0e59 TW |
344 | return -EMSGSIZE; |
345 | ||
fb7d879f | 346 | /* complete all waiting for write CB */ |
2bf94cab | 347 | dev_dbg(dev->dev, "complete all waiting for write cb.\n"); |
fb7d879f OW |
348 | |
349 | list = &dev->write_waiting_list; | |
6220d6a0 TW |
350 | list_for_each_entry_safe(cb, next, &list->list, list) { |
351 | cl = cb->cl; | |
b7cd2d9f TW |
352 | |
353 | cl->status = 0; | |
c54bf3ab TW |
354 | cl_dbg(dev, cl, "MEI WRITE COMPLETE\n"); |
355 | cl->writing_state = MEI_WRITE_COMPLETE; | |
356 | list_move_tail(&cb->list, &cmpl_list->list); | |
fb7d879f OW |
357 | } |
358 | ||
c216fdeb TW |
359 | if (dev->wd_state == MEI_WD_STOPPING) { |
360 | dev->wd_state = MEI_WD_IDLE; | |
5877255d | 361 | wake_up(&dev->wait_stop_wd); |
fb7d879f OW |
362 | } |
363 | ||
64092858 | 364 | if (mei_cl_is_connected(&dev->wd_cl)) { |
fb7d879f | 365 | if (dev->wd_pending && |
90e0b5f1 | 366 | mei_cl_flow_ctrl_creds(&dev->wd_cl) > 0) { |
b6d81fd6 TW |
367 | ret = mei_wd_send(dev); |
368 | if (ret) | |
369 | return ret; | |
eb9af0ac | 370 | dev->wd_pending = false; |
fb7d879f OW |
371 | } |
372 | } | |
fb7d879f OW |
373 | |
374 | /* complete control write list CB */ | |
2bf94cab | 375 | dev_dbg(dev->dev, "complete control write list cb.\n"); |
6220d6a0 TW |
376 | list_for_each_entry_safe(cb, next, &dev->ctrl_wr_list.list, list) { |
377 | cl = cb->cl; | |
6220d6a0 | 378 | switch (cb->fop_type) { |
5a8373fb | 379 | case MEI_FOP_DISCONNECT: |
c8372094 | 380 | /* send disconnect message */ |
5a8373fb | 381 | ret = mei_cl_irq_disconnect(cl, cb, cmpl_list); |
c8372094 TW |
382 | if (ret) |
383 | return ret; | |
fb7d879f | 384 | |
c8372094 | 385 | break; |
4b8960b4 | 386 | case MEI_FOP_READ: |
c8372094 | 387 | /* send flow control message */ |
9d098192 | 388 | ret = mei_cl_irq_read(cl, cb, cmpl_list); |
c8372094 TW |
389 | if (ret) |
390 | return ret; | |
fb7d879f | 391 | |
c8372094 | 392 | break; |
02a7eecc | 393 | case MEI_FOP_CONNECT: |
c8372094 | 394 | /* connect message */ |
9d098192 | 395 | ret = mei_cl_irq_connect(cl, cb, cmpl_list); |
c8372094 TW |
396 | if (ret) |
397 | return ret; | |
fb7d879f | 398 | |
c8372094 | 399 | break; |
6bb948c9 TW |
400 | case MEI_FOP_DISCONNECT_RSP: |
401 | /* send disconnect resp */ | |
9d098192 | 402 | ret = mei_cl_irq_disconnect_rsp(cl, cb, cmpl_list); |
6bb948c9 TW |
403 | if (ret) |
404 | return ret; | |
31a5ef24 | 405 | break; |
c8372094 TW |
406 | default: |
407 | BUG(); | |
fb7d879f | 408 | } |
c8372094 | 409 | |
fb7d879f OW |
410 | } |
411 | /* complete write list CB */ | |
2bf94cab | 412 | dev_dbg(dev->dev, "complete write list cb.\n"); |
6220d6a0 TW |
413 | list_for_each_entry_safe(cb, next, &dev->write_list.list, list) { |
414 | cl = cb->cl; | |
be9d87a7 | 415 | if (cl == &dev->iamthif_cl) |
9d098192 | 416 | ret = mei_amthif_irq_write(cl, cb, cmpl_list); |
be9d87a7 | 417 | else |
9d098192 | 418 | ret = mei_cl_irq_write(cl, cb, cmpl_list); |
be9d87a7 TW |
419 | if (ret) |
420 | return ret; | |
fb7d879f OW |
421 | } |
422 | return 0; | |
423 | } | |
40e0b67b | 424 | EXPORT_SYMBOL_GPL(mei_irq_write_handler); |
fb7d879f OW |
425 | |
426 | ||
427 | ||
428 | /** | |
429 | * mei_timer - timer function. | |
430 | * | |
431 | * @work: pointer to the work_struct structure | |
432 | * | |
fb7d879f | 433 | */ |
a61c6530 | 434 | void mei_timer(struct work_struct *work) |
fb7d879f OW |
435 | { |
436 | unsigned long timeout; | |
31f88f57 | 437 | struct mei_cl *cl; |
fb7d879f OW |
438 | |
439 | struct mei_device *dev = container_of(work, | |
a61c6530 | 440 | struct mei_device, timer_work.work); |
fb7d879f OW |
441 | |
442 | ||
443 | mutex_lock(&dev->device_lock); | |
66ae460b TW |
444 | |
445 | /* Catch interrupt stalls during HBM init handshake */ | |
446 | if (dev->dev_state == MEI_DEV_INIT_CLIENTS && | |
447 | dev->hbm_state != MEI_HBM_IDLE) { | |
448 | ||
449 | if (dev->init_clients_timer) { | |
450 | if (--dev->init_clients_timer == 0) { | |
2bf94cab | 451 | dev_err(dev->dev, "timer: init clients timeout hbm_state = %d.\n", |
66ae460b | 452 | dev->hbm_state); |
33ec0826 | 453 | mei_reset(dev); |
66ae460b | 454 | goto out; |
fb7d879f OW |
455 | } |
456 | } | |
fb7d879f | 457 | } |
66ae460b TW |
458 | |
459 | if (dev->dev_state != MEI_DEV_ENABLED) | |
460 | goto out; | |
461 | ||
fb7d879f | 462 | /*** connect/disconnect timeouts ***/ |
31f88f57 TW |
463 | list_for_each_entry(cl, &dev->file_list, link) { |
464 | if (cl->timer_count) { | |
465 | if (--cl->timer_count == 0) { | |
2bf94cab | 466 | dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); |
33ec0826 | 467 | mei_reset(dev); |
fb7d879f OW |
468 | goto out; |
469 | } | |
470 | } | |
471 | } | |
472 | ||
64092858 TW |
473 | if (!mei_cl_is_connected(&dev->iamthif_cl)) |
474 | goto out; | |
475 | ||
fb7d879f OW |
476 | if (dev->iamthif_stall_timer) { |
477 | if (--dev->iamthif_stall_timer == 0) { | |
2bf94cab | 478 | dev_err(dev->dev, "timer: amthif hanged.\n"); |
33ec0826 | 479 | mei_reset(dev); |
eb9af0ac | 480 | dev->iamthif_canceled = false; |
fb7d879f OW |
481 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
482 | dev->iamthif_timer = 0; | |
483 | ||
601a1efa TW |
484 | mei_io_cb_free(dev->iamthif_current_cb); |
485 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
486 | |
487 | dev->iamthif_file_object = NULL; | |
19838fb8 | 488 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
489 | } |
490 | } | |
491 | ||
492 | if (dev->iamthif_timer) { | |
493 | ||
494 | timeout = dev->iamthif_timer + | |
3870c320 | 495 | mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); |
fb7d879f | 496 | |
2bf94cab | 497 | dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", |
fb7d879f | 498 | dev->iamthif_timer); |
2bf94cab TW |
499 | dev_dbg(dev->dev, "timeout = %ld\n", timeout); |
500 | dev_dbg(dev->dev, "jiffies = %ld\n", jiffies); | |
fb7d879f OW |
501 | if (time_after(jiffies, timeout)) { |
502 | /* | |
503 | * User didn't read the AMTHI data on time (15sec) | |
504 | * freeing AMTHI for other requests | |
505 | */ | |
506 | ||
2bf94cab | 507 | dev_dbg(dev->dev, "freeing AMTHI for other requests\n"); |
fb7d879f | 508 | |
5456796b AU |
509 | mei_io_list_flush(&dev->amthif_rd_complete_list, |
510 | &dev->iamthif_cl); | |
601a1efa TW |
511 | mei_io_cb_free(dev->iamthif_current_cb); |
512 | dev->iamthif_current_cb = NULL; | |
fb7d879f OW |
513 | |
514 | dev->iamthif_file_object->private_data = NULL; | |
515 | dev->iamthif_file_object = NULL; | |
fb7d879f | 516 | dev->iamthif_timer = 0; |
19838fb8 | 517 | mei_amthif_run_next_cmd(dev); |
fb7d879f OW |
518 | |
519 | } | |
520 | } | |
521 | out: | |
33ec0826 TW |
522 | if (dev->dev_state != MEI_DEV_DISABLED) |
523 | schedule_delayed_work(&dev->timer_work, 2 * HZ); | |
441ab50f | 524 | mutex_unlock(&dev->device_lock); |
fb7d879f | 525 | } |