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