Commit | Line | Data |
---|---|---|
19838fb8 TW |
1 | /* |
2 | * | |
3 | * Intel Management Engine Interface (Intel MEI) Linux driver | |
4 | * Copyright (c) 2003-2012, Intel Corporation. | |
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 | #include <linux/kernel.h> | |
18 | #include <linux/fs.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/types.h> | |
21 | #include <linux/fcntl.h> | |
19838fb8 TW |
22 | #include <linux/ioctl.h> |
23 | #include <linux/cdev.h> | |
24 | #include <linux/list.h> | |
25 | #include <linux/delay.h> | |
26 | #include <linux/sched.h> | |
27 | #include <linux/uuid.h> | |
28 | #include <linux/jiffies.h> | |
29 | #include <linux/uaccess.h> | |
1f180359 | 30 | #include <linux/slab.h> |
19838fb8 | 31 | |
47a73801 | 32 | #include <linux/mei.h> |
19838fb8 TW |
33 | |
34 | #include "mei_dev.h" | |
0edb23fc | 35 | #include "hbm.h" |
90e0b5f1 | 36 | #include "client.h" |
19838fb8 | 37 | |
1a1aca42 TW |
38 | const uuid_le mei_amthif_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, |
39 | 0xac, 0xa8, 0x46, 0xe0, | |
40 | 0xff, 0x65, 0x81, 0x4c); | |
19838fb8 TW |
41 | |
42 | /** | |
43 | * mei_amthif_reset_params - initializes mei device iamthif | |
44 | * | |
45 | * @dev: the device structure | |
46 | */ | |
47 | void mei_amthif_reset_params(struct mei_device *dev) | |
48 | { | |
49 | /* reset iamthif parameters. */ | |
50 | dev->iamthif_current_cb = NULL; | |
51 | dev->iamthif_msg_buf_size = 0; | |
52 | dev->iamthif_msg_buf_index = 0; | |
53 | dev->iamthif_canceled = false; | |
54 | dev->iamthif_ioctl = false; | |
55 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
56 | dev->iamthif_timer = 0; | |
4a704575 | 57 | dev->iamthif_stall_timer = 0; |
22f96a0e | 58 | dev->iamthif_open_count = 0; |
19838fb8 TW |
59 | } |
60 | ||
61 | /** | |
393b148f | 62 | * mei_amthif_host_init - mei initialization amthif client. |
19838fb8 TW |
63 | * |
64 | * @dev: the device structure | |
65 | * | |
ce23139c | 66 | * Return: 0 on success, <0 on failure. |
19838fb8 | 67 | */ |
781d0d89 | 68 | int mei_amthif_host_init(struct mei_device *dev) |
19838fb8 | 69 | { |
781d0d89 | 70 | struct mei_cl *cl = &dev->iamthif_cl; |
d320832f | 71 | struct mei_me_client *me_cl; |
19838fb8 | 72 | unsigned char *msg_buf; |
d320832f | 73 | int ret; |
19838fb8 | 74 | |
6222f7bf TW |
75 | dev->iamthif_state = MEI_IAMTHIF_IDLE; |
76 | ||
781d0d89 | 77 | mei_cl_init(cl, dev); |
19838fb8 | 78 | |
d320832f TW |
79 | me_cl = mei_me_cl_by_uuid(dev, &mei_amthif_guid); |
80 | if (!me_cl) { | |
2bf94cab | 81 | dev_info(dev->dev, "amthif: failed to find the client"); |
7ca96aa2 | 82 | return -ENOTTY; |
19838fb8 TW |
83 | } |
84 | ||
d320832f | 85 | cl->me_client_id = me_cl->client_id; |
d880f329 | 86 | cl->cl_uuid = me_cl->props.protocol_name; |
781d0d89 | 87 | |
19838fb8 TW |
88 | /* Assign iamthif_mtu to the value received from ME */ |
89 | ||
d320832f | 90 | dev->iamthif_mtu = me_cl->props.max_msg_length; |
2bf94cab | 91 | dev_dbg(dev->dev, "IAMTHIF_MTU = %d\n", dev->iamthif_mtu); |
19838fb8 TW |
92 | |
93 | kfree(dev->iamthif_msg_buf); | |
94 | dev->iamthif_msg_buf = NULL; | |
95 | ||
96 | /* allocate storage for ME message buffer */ | |
97 | msg_buf = kcalloc(dev->iamthif_mtu, | |
98 | sizeof(unsigned char), GFP_KERNEL); | |
79563db9 TW |
99 | if (!msg_buf) { |
100 | ret = -ENOMEM; | |
101 | goto out; | |
102 | } | |
19838fb8 TW |
103 | |
104 | dev->iamthif_msg_buf = msg_buf; | |
105 | ||
781d0d89 | 106 | ret = mei_cl_link(cl, MEI_IAMTHIF_HOST_CLIENT_ID); |
781d0d89 | 107 | if (ret < 0) { |
79563db9 TW |
108 | dev_err(dev->dev, "amthif: failed cl_link %d\n", ret); |
109 | goto out; | |
781d0d89 TW |
110 | } |
111 | ||
64092858 TW |
112 | ret = mei_cl_connect(cl, NULL); |
113 | ||
114 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
115 | ||
79563db9 TW |
116 | out: |
117 | mei_me_cl_put(me_cl); | |
64092858 | 118 | return ret; |
19838fb8 TW |
119 | } |
120 | ||
121 | /** | |
122 | * mei_amthif_find_read_list_entry - finds a amthilist entry for current file | |
123 | * | |
124 | * @dev: the device structure | |
125 | * @file: pointer to file object | |
126 | * | |
a8605ea2 | 127 | * Return: returned a list entry on success, NULL on failure. |
19838fb8 TW |
128 | */ |
129 | struct mei_cl_cb *mei_amthif_find_read_list_entry(struct mei_device *dev, | |
130 | struct file *file) | |
131 | { | |
31f88f57 | 132 | struct mei_cl_cb *cb; |
92db1555 | 133 | |
05e314e2 AU |
134 | list_for_each_entry(cb, &dev->amthif_rd_complete_list.list, list) |
135 | if (cb->file_object == file) | |
31f88f57 | 136 | return cb; |
19838fb8 TW |
137 | return NULL; |
138 | } | |
139 | ||
140 | ||
141 | /** | |
142 | * mei_amthif_read - read data from AMTHIF client | |
143 | * | |
144 | * @dev: the device structure | |
19838fb8 | 145 | * @file: pointer to file object |
a8605ea2 | 146 | * @ubuf: pointer to user data in user space |
19838fb8 TW |
147 | * @length: data length to read |
148 | * @offset: data read offset | |
149 | * | |
150 | * Locking: called under "dev->device_lock" lock | |
151 | * | |
a8605ea2 | 152 | * Return: |
19838fb8 TW |
153 | * returned data length on success, |
154 | * zero if no data to read, | |
155 | * negative on failure. | |
156 | */ | |
157 | int mei_amthif_read(struct mei_device *dev, struct file *file, | |
158 | char __user *ubuf, size_t length, loff_t *offset) | |
159 | { | |
19838fb8 | 160 | struct mei_cl *cl = file->private_data; |
d320832f | 161 | struct mei_cl_cb *cb; |
19838fb8 | 162 | unsigned long timeout; |
d320832f TW |
163 | int rets; |
164 | int wait_ret; | |
19838fb8 | 165 | |
83ce0741 | 166 | /* Only possible if we are in timeout */ |
05e314e2 | 167 | if (!cl) { |
2bf94cab | 168 | dev_err(dev->dev, "bad file ext.\n"); |
7ca96aa2 | 169 | return -ETIME; |
19838fb8 TW |
170 | } |
171 | ||
2bf94cab | 172 | dev_dbg(dev->dev, "checking amthif data\n"); |
19838fb8 TW |
173 | cb = mei_amthif_find_read_list_entry(dev, file); |
174 | ||
175 | /* Check for if we can block or not*/ | |
176 | if (cb == NULL && file->f_flags & O_NONBLOCK) | |
177 | return -EAGAIN; | |
178 | ||
179 | ||
2bf94cab | 180 | dev_dbg(dev->dev, "waiting for amthif data\n"); |
19838fb8 TW |
181 | while (cb == NULL) { |
182 | /* unlock the Mutex */ | |
183 | mutex_unlock(&dev->device_lock); | |
184 | ||
185 | wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, | |
186 | (cb = mei_amthif_find_read_list_entry(dev, file))); | |
187 | ||
e6028db0 AK |
188 | /* Locking again the Mutex */ |
189 | mutex_lock(&dev->device_lock); | |
190 | ||
19838fb8 TW |
191 | if (wait_ret) |
192 | return -ERESTARTSYS; | |
193 | ||
2bf94cab | 194 | dev_dbg(dev->dev, "woke up from sleep\n"); |
19838fb8 TW |
195 | } |
196 | ||
197 | ||
2bf94cab | 198 | dev_dbg(dev->dev, "Got amthif data\n"); |
19838fb8 TW |
199 | dev->iamthif_timer = 0; |
200 | ||
201 | if (cb) { | |
202 | timeout = cb->read_time + | |
203 | mei_secs_to_jiffies(MEI_IAMTHIF_READ_TIMER); | |
2bf94cab | 204 | dev_dbg(dev->dev, "amthif timeout = %lud\n", |
19838fb8 TW |
205 | timeout); |
206 | ||
207 | if (time_after(jiffies, timeout)) { | |
2bf94cab | 208 | dev_dbg(dev->dev, "amthif Time out\n"); |
19838fb8 TW |
209 | /* 15 sec for the message has expired */ |
210 | list_del(&cb->list); | |
7ca96aa2 | 211 | rets = -ETIME; |
19838fb8 TW |
212 | goto free; |
213 | } | |
214 | } | |
215 | /* if the whole message will fit remove it from the list */ | |
216 | if (cb->buf_idx >= *offset && length >= (cb->buf_idx - *offset)) | |
217 | list_del(&cb->list); | |
218 | else if (cb->buf_idx > 0 && cb->buf_idx <= *offset) { | |
219 | /* end of the message has been reached */ | |
220 | list_del(&cb->list); | |
221 | rets = 0; | |
222 | goto free; | |
223 | } | |
224 | /* else means that not full buffer will be read and do not | |
225 | * remove message from deletion list | |
226 | */ | |
227 | ||
2bf94cab | 228 | dev_dbg(dev->dev, "amthif cb->response_buffer size - %d\n", |
19838fb8 | 229 | cb->response_buffer.size); |
2bf94cab | 230 | dev_dbg(dev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx); |
19838fb8 | 231 | |
83ce0741 | 232 | /* length is being truncated to PAGE_SIZE, however, |
19838fb8 TW |
233 | * the buf_idx may point beyond */ |
234 | length = min_t(size_t, length, (cb->buf_idx - *offset)); | |
235 | ||
dbac4745 | 236 | if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) { |
2bf94cab | 237 | dev_dbg(dev->dev, "failed to copy data to userland\n"); |
19838fb8 | 238 | rets = -EFAULT; |
34e267d0 | 239 | } else { |
19838fb8 TW |
240 | rets = length; |
241 | if ((*offset + length) < cb->buf_idx) { | |
242 | *offset += length; | |
243 | goto out; | |
244 | } | |
245 | } | |
246 | free: | |
2bf94cab | 247 | dev_dbg(dev->dev, "free amthif cb memory.\n"); |
19838fb8 TW |
248 | *offset = 0; |
249 | mei_io_cb_free(cb); | |
250 | out: | |
251 | return rets; | |
252 | } | |
253 | ||
254 | /** | |
ab5c4a56 | 255 | * mei_amthif_send_cmd - send amthif command to the ME |
19838fb8 TW |
256 | * |
257 | * @dev: the device structure | |
258 | * @cb: mei call back struct | |
259 | * | |
a8605ea2 | 260 | * Return: 0 on success, <0 on failure. |
ab5c4a56 | 261 | * |
19838fb8 | 262 | */ |
ab5c4a56 | 263 | static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb) |
19838fb8 TW |
264 | { |
265 | struct mei_msg_hdr mei_hdr; | |
3d32cf02 | 266 | struct mei_cl *cl; |
19838fb8 TW |
267 | int ret; |
268 | ||
269 | if (!dev || !cb) | |
270 | return -ENODEV; | |
271 | ||
2bf94cab | 272 | dev_dbg(dev->dev, "write data to amthif client.\n"); |
19838fb8 TW |
273 | |
274 | dev->iamthif_state = MEI_IAMTHIF_WRITING; | |
275 | dev->iamthif_current_cb = cb; | |
276 | dev->iamthif_file_object = cb->file_object; | |
277 | dev->iamthif_canceled = false; | |
278 | dev->iamthif_ioctl = true; | |
279 | dev->iamthif_msg_buf_size = cb->request_buffer.size; | |
280 | memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, | |
281 | cb->request_buffer.size); | |
3d32cf02 | 282 | cl = &dev->iamthif_cl; |
19838fb8 | 283 | |
3d32cf02 | 284 | ret = mei_cl_flow_ctrl_creds(cl); |
19838fb8 TW |
285 | if (ret < 0) |
286 | return ret; | |
287 | ||
6aae48ff | 288 | if (ret && mei_hbuf_acquire(dev)) { |
19838fb8 | 289 | ret = 0; |
827eef51 TW |
290 | if (cb->request_buffer.size > mei_hbuf_max_len(dev)) { |
291 | mei_hdr.length = mei_hbuf_max_len(dev); | |
19838fb8 TW |
292 | mei_hdr.msg_complete = 0; |
293 | } else { | |
294 | mei_hdr.length = cb->request_buffer.size; | |
295 | mei_hdr.msg_complete = 1; | |
296 | } | |
297 | ||
3d32cf02 TW |
298 | mei_hdr.host_addr = cl->host_client_id; |
299 | mei_hdr.me_addr = cl->me_client_id; | |
19838fb8 | 300 | mei_hdr.reserved = 0; |
479327fc | 301 | mei_hdr.internal = 0; |
19838fb8 | 302 | dev->iamthif_msg_buf_index += mei_hdr.length; |
2ebf8c94 TW |
303 | ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf); |
304 | if (ret) | |
305 | return ret; | |
19838fb8 TW |
306 | |
307 | if (mei_hdr.msg_complete) { | |
3d32cf02 | 308 | if (mei_cl_flow_ctrl_reduce(cl)) |
2ebf8c94 | 309 | return -EIO; |
19838fb8 TW |
310 | dev->iamthif_flow_control_pending = true; |
311 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; | |
2bf94cab | 312 | dev_dbg(dev->dev, "add amthif cb to write waiting list\n"); |
19838fb8 TW |
313 | dev->iamthif_current_cb = cb; |
314 | dev->iamthif_file_object = cb->file_object; | |
315 | list_add_tail(&cb->list, &dev->write_waiting_list.list); | |
316 | } else { | |
2bf94cab | 317 | dev_dbg(dev->dev, "message does not complete, so add amthif cb to write list.\n"); |
19838fb8 TW |
318 | list_add_tail(&cb->list, &dev->write_list.list); |
319 | } | |
320 | } else { | |
19838fb8 TW |
321 | list_add_tail(&cb->list, &dev->write_list.list); |
322 | } | |
323 | return 0; | |
324 | } | |
325 | ||
ab5c4a56 TW |
326 | /** |
327 | * mei_amthif_write - write amthif data to amthif client | |
328 | * | |
329 | * @dev: the device structure | |
330 | * @cb: mei call back struct | |
331 | * | |
a8605ea2 | 332 | * Return: 0 on success, <0 on failure. |
ab5c4a56 TW |
333 | * |
334 | */ | |
335 | int mei_amthif_write(struct mei_device *dev, struct mei_cl_cb *cb) | |
336 | { | |
337 | int ret; | |
338 | ||
339 | if (!dev || !cb) | |
340 | return -ENODEV; | |
341 | ||
342 | ret = mei_io_cb_alloc_resp_buf(cb, dev->iamthif_mtu); | |
343 | if (ret) | |
344 | return ret; | |
345 | ||
02a7eecc | 346 | cb->fop_type = MEI_FOP_WRITE; |
ab5c4a56 | 347 | |
e773efc4 | 348 | if (!list_empty(&dev->amthif_cmd_list.list) || |
ab5c4a56 | 349 | dev->iamthif_state != MEI_IAMTHIF_IDLE) { |
2bf94cab | 350 | dev_dbg(dev->dev, |
ab5c4a56 | 351 | "amthif state = %d\n", dev->iamthif_state); |
2bf94cab | 352 | dev_dbg(dev->dev, "AMTHIF: add cb to the wait list\n"); |
e773efc4 | 353 | list_add_tail(&cb->list, &dev->amthif_cmd_list.list); |
ab5c4a56 TW |
354 | return 0; |
355 | } | |
356 | return mei_amthif_send_cmd(dev, cb); | |
357 | } | |
19838fb8 | 358 | /** |
ce23139c | 359 | * mei_amthif_run_next_cmd - send next amt command from queue |
19838fb8 TW |
360 | * |
361 | * @dev: the device structure | |
19838fb8 TW |
362 | */ |
363 | void mei_amthif_run_next_cmd(struct mei_device *dev) | |
364 | { | |
05e314e2 | 365 | struct mei_cl_cb *cb; |
140c7553 | 366 | int ret; |
19838fb8 TW |
367 | |
368 | if (!dev) | |
369 | return; | |
370 | ||
371 | dev->iamthif_msg_buf_size = 0; | |
372 | dev->iamthif_msg_buf_index = 0; | |
373 | dev->iamthif_canceled = false; | |
374 | dev->iamthif_ioctl = true; | |
375 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
376 | dev->iamthif_timer = 0; | |
377 | dev->iamthif_file_object = NULL; | |
378 | ||
2bf94cab | 379 | dev_dbg(dev->dev, "complete amthif cmd_list cb.\n"); |
19838fb8 | 380 | |
140c7553 AU |
381 | cb = list_first_entry_or_null(&dev->amthif_cmd_list.list, |
382 | typeof(*cb), list); | |
383 | if (!cb) | |
384 | return; | |
385 | list_del(&cb->list); | |
386 | ret = mei_amthif_send_cmd(dev, cb); | |
387 | if (ret) | |
388 | dev_warn(dev->dev, "amthif write failed status = %d\n", ret); | |
19838fb8 TW |
389 | } |
390 | ||
744f0f2f TW |
391 | |
392 | unsigned int mei_amthif_poll(struct mei_device *dev, | |
393 | struct file *file, poll_table *wait) | |
394 | { | |
395 | unsigned int mask = 0; | |
b950ac1d | 396 | |
744f0f2f | 397 | poll_wait(file, &dev->iamthif_cl.wait, wait); |
b950ac1d | 398 | |
744f0f2f | 399 | mutex_lock(&dev->device_lock); |
b950ac1d TW |
400 | if (!mei_cl_is_connected(&dev->iamthif_cl)) { |
401 | ||
402 | mask = POLLERR; | |
403 | ||
404 | } else if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE && | |
405 | dev->iamthif_file_object == file) { | |
406 | ||
744f0f2f | 407 | mask |= (POLLIN | POLLRDNORM); |
2bf94cab | 408 | dev_dbg(dev->dev, "run next amthif cb\n"); |
744f0f2f TW |
409 | mei_amthif_run_next_cmd(dev); |
410 | } | |
b950ac1d TW |
411 | mutex_unlock(&dev->device_lock); |
412 | ||
744f0f2f TW |
413 | return mask; |
414 | } | |
415 | ||
416 | ||
417 | ||
19838fb8 | 418 | /** |
9d098192 | 419 | * mei_amthif_irq_write - write iamthif command in irq thread context. |
19838fb8 | 420 | * |
19838fb8 | 421 | * @cl: private data of the file object. |
a8605ea2 | 422 | * @cb: callback block. |
19838fb8 TW |
423 | * @cmpl_list: complete list. |
424 | * | |
a8605ea2 | 425 | * Return: 0, OK; otherwise, error. |
19838fb8 | 426 | */ |
9d098192 TW |
427 | int mei_amthif_irq_write(struct mei_cl *cl, struct mei_cl_cb *cb, |
428 | struct mei_cl_cb *cmpl_list) | |
19838fb8 | 429 | { |
6220d6a0 | 430 | struct mei_device *dev = cl->dev; |
e46f1874 | 431 | struct mei_msg_hdr mei_hdr; |
24c656e5 | 432 | size_t len = dev->iamthif_msg_buf_size - dev->iamthif_msg_buf_index; |
c8c8d080 | 433 | u32 msg_slots = mei_data2slots(len); |
9d098192 | 434 | int slots; |
2ebf8c94 | 435 | int rets; |
19838fb8 | 436 | |
136698e5 TW |
437 | rets = mei_cl_flow_ctrl_creds(cl); |
438 | if (rets < 0) | |
439 | return rets; | |
440 | ||
441 | if (rets == 0) { | |
442 | cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); | |
443 | return 0; | |
444 | } | |
445 | ||
e46f1874 TW |
446 | mei_hdr.host_addr = cl->host_client_id; |
447 | mei_hdr.me_addr = cl->me_client_id; | |
448 | mei_hdr.reserved = 0; | |
479327fc | 449 | mei_hdr.internal = 0; |
24c656e5 | 450 | |
9d098192 TW |
451 | slots = mei_hbuf_empty_slots(dev); |
452 | ||
453 | if (slots >= msg_slots) { | |
e46f1874 TW |
454 | mei_hdr.length = len; |
455 | mei_hdr.msg_complete = 1; | |
24c656e5 | 456 | /* Split the message only if we can write the whole host buffer */ |
9d098192 TW |
457 | } else if (slots == dev->hbuf_depth) { |
458 | msg_slots = slots; | |
459 | len = (slots * sizeof(u32)) - sizeof(struct mei_msg_hdr); | |
e46f1874 TW |
460 | mei_hdr.length = len; |
461 | mei_hdr.msg_complete = 0; | |
24c656e5 TW |
462 | } else { |
463 | /* wait for next time the host buffer is empty */ | |
464 | return 0; | |
465 | } | |
19838fb8 | 466 | |
2bf94cab | 467 | dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(&mei_hdr)); |
19838fb8 | 468 | |
2ebf8c94 TW |
469 | rets = mei_write_message(dev, &mei_hdr, |
470 | dev->iamthif_msg_buf + dev->iamthif_msg_buf_index); | |
471 | if (rets) { | |
472 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
473 | cl->status = rets; | |
474 | list_del(&cb->list); | |
475 | return rets; | |
24c656e5 | 476 | } |
19838fb8 | 477 | |
90e0b5f1 | 478 | if (mei_cl_flow_ctrl_reduce(cl)) |
2ebf8c94 | 479 | return -EIO; |
19838fb8 | 480 | |
e46f1874 | 481 | dev->iamthif_msg_buf_index += mei_hdr.length; |
24c656e5 | 482 | cl->status = 0; |
19838fb8 | 483 | |
e46f1874 | 484 | if (mei_hdr.msg_complete) { |
24c656e5 TW |
485 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; |
486 | dev->iamthif_flow_control_pending = true; | |
487 | ||
1a1aca42 | 488 | /* save iamthif cb sent to amthif client */ |
24c656e5 TW |
489 | cb->buf_idx = dev->iamthif_msg_buf_index; |
490 | dev->iamthif_current_cb = cb; | |
491 | ||
492 | list_move_tail(&cb->list, &dev->write_waiting_list.list); | |
19838fb8 TW |
493 | } |
494 | ||
24c656e5 | 495 | |
19838fb8 TW |
496 | return 0; |
497 | } | |
498 | ||
499 | /** | |
ce23139c | 500 | * mei_amthif_irq_read_msg - read routine after ISR to |
1a1aca42 | 501 | * handle the read amthif message |
19838fb8 | 502 | * |
19838fb8 | 503 | * @dev: the device structure |
1a1aca42 | 504 | * @mei_hdr: header of amthif message |
5ceb46e2 | 505 | * @complete_list: An instance of our list structure |
19838fb8 | 506 | * |
a8605ea2 | 507 | * Return: 0 on success, <0 on failure. |
19838fb8 | 508 | */ |
5ceb46e2 TW |
509 | int mei_amthif_irq_read_msg(struct mei_device *dev, |
510 | struct mei_msg_hdr *mei_hdr, | |
511 | struct mei_cl_cb *complete_list) | |
19838fb8 | 512 | { |
19838fb8 TW |
513 | struct mei_cl_cb *cb; |
514 | unsigned char *buffer; | |
515 | ||
516 | BUG_ON(mei_hdr->me_addr != dev->iamthif_cl.me_client_id); | |
517 | BUG_ON(dev->iamthif_state != MEI_IAMTHIF_READING); | |
518 | ||
519 | buffer = dev->iamthif_msg_buf + dev->iamthif_msg_buf_index; | |
520 | BUG_ON(dev->iamthif_mtu < dev->iamthif_msg_buf_index + mei_hdr->length); | |
521 | ||
522 | mei_read_slots(dev, buffer, mei_hdr->length); | |
523 | ||
524 | dev->iamthif_msg_buf_index += mei_hdr->length; | |
525 | ||
526 | if (!mei_hdr->msg_complete) | |
527 | return 0; | |
528 | ||
2bf94cab | 529 | dev_dbg(dev->dev, "amthif_message_buffer_index =%d\n", |
19838fb8 TW |
530 | mei_hdr->length); |
531 | ||
2bf94cab | 532 | dev_dbg(dev->dev, "completed amthif read.\n "); |
19838fb8 TW |
533 | if (!dev->iamthif_current_cb) |
534 | return -ENODEV; | |
535 | ||
536 | cb = dev->iamthif_current_cb; | |
537 | dev->iamthif_current_cb = NULL; | |
538 | ||
19838fb8 TW |
539 | dev->iamthif_stall_timer = 0; |
540 | cb->buf_idx = dev->iamthif_msg_buf_index; | |
541 | cb->read_time = jiffies; | |
05e314e2 | 542 | if (dev->iamthif_ioctl) { |
19838fb8 | 543 | /* found the iamthif cb */ |
2bf94cab TW |
544 | dev_dbg(dev->dev, "complete the amthif read cb.\n "); |
545 | dev_dbg(dev->dev, "add the amthif read cb to complete.\n "); | |
19838fb8 TW |
546 | list_add_tail(&cb->list, &complete_list->list); |
547 | } | |
548 | return 0; | |
549 | } | |
550 | ||
551 | /** | |
552 | * mei_amthif_irq_read - prepares to read amthif data. | |
553 | * | |
554 | * @dev: the device structure. | |
555 | * @slots: free slots. | |
556 | * | |
a8605ea2 | 557 | * Return: 0, OK; otherwise, error. |
19838fb8 TW |
558 | */ |
559 | int mei_amthif_irq_read(struct mei_device *dev, s32 *slots) | |
560 | { | |
c8c8d080 | 561 | u32 msg_slots = mei_data2slots(sizeof(struct hbm_flow_control)); |
19838fb8 | 562 | |
c8c8d080 | 563 | if (*slots < msg_slots) |
19838fb8 | 564 | return -EMSGSIZE; |
c8c8d080 TW |
565 | |
566 | *slots -= msg_slots; | |
567 | ||
8120e720 | 568 | if (mei_hbm_cl_flow_control_req(dev, &dev->iamthif_cl)) { |
2bf94cab | 569 | dev_dbg(dev->dev, "iamthif flow control failed\n"); |
19838fb8 TW |
570 | return -EIO; |
571 | } | |
572 | ||
2bf94cab | 573 | dev_dbg(dev->dev, "iamthif flow control success\n"); |
19838fb8 TW |
574 | dev->iamthif_state = MEI_IAMTHIF_READING; |
575 | dev->iamthif_flow_control_pending = false; | |
576 | dev->iamthif_msg_buf_index = 0; | |
577 | dev->iamthif_msg_buf_size = 0; | |
578 | dev->iamthif_stall_timer = MEI_IAMTHIF_STALL_TIMER; | |
330dd7da | 579 | dev->hbuf_is_ready = mei_hbuf_is_ready(dev); |
19838fb8 TW |
580 | return 0; |
581 | } | |
582 | ||
583 | /** | |
584 | * mei_amthif_complete - complete amthif callback. | |
585 | * | |
586 | * @dev: the device structure. | |
a8605ea2 | 587 | * @cb: callback block. |
19838fb8 TW |
588 | */ |
589 | void mei_amthif_complete(struct mei_device *dev, struct mei_cl_cb *cb) | |
590 | { | |
591 | if (dev->iamthif_canceled != 1) { | |
592 | dev->iamthif_state = MEI_IAMTHIF_READ_COMPLETE; | |
593 | dev->iamthif_stall_timer = 0; | |
594 | memcpy(cb->response_buffer.data, | |
595 | dev->iamthif_msg_buf, | |
596 | dev->iamthif_msg_buf_index); | |
e773efc4 | 597 | list_add_tail(&cb->list, &dev->amthif_rd_complete_list.list); |
2bf94cab | 598 | dev_dbg(dev->dev, "amthif read completed\n"); |
19838fb8 | 599 | dev->iamthif_timer = jiffies; |
2bf94cab | 600 | dev_dbg(dev->dev, "dev->iamthif_timer = %ld\n", |
19838fb8 TW |
601 | dev->iamthif_timer); |
602 | } else { | |
603 | mei_amthif_run_next_cmd(dev); | |
604 | } | |
605 | ||
2bf94cab | 606 | dev_dbg(dev->dev, "completing amthif call back.\n"); |
19838fb8 TW |
607 | wake_up_interruptible(&dev->iamthif_cl.wait); |
608 | } | |
609 | ||
a562d5c2 TW |
610 | /** |
611 | * mei_clear_list - removes all callbacks associated with file | |
612 | * from mei_cb_list | |
613 | * | |
614 | * @dev: device structure. | |
615 | * @file: file structure | |
616 | * @mei_cb_list: callbacks list | |
617 | * | |
618 | * mei_clear_list is called to clear resources associated with file | |
619 | * when application calls close function or Ctrl-C was pressed | |
620 | * | |
a8605ea2 | 621 | * Return: true if callback removed from the list, false otherwise |
a562d5c2 TW |
622 | */ |
623 | static bool mei_clear_list(struct mei_device *dev, | |
624 | const struct file *file, struct list_head *mei_cb_list) | |
625 | { | |
626 | struct mei_cl_cb *cb_pos = NULL; | |
627 | struct mei_cl_cb *cb_next = NULL; | |
628 | bool removed = false; | |
629 | ||
630 | /* list all list member */ | |
631 | list_for_each_entry_safe(cb_pos, cb_next, mei_cb_list, list) { | |
632 | /* check if list member associated with a file */ | |
633 | if (file == cb_pos->file_object) { | |
634 | /* remove member from the list */ | |
635 | list_del(&cb_pos->list); | |
636 | /* check if cb equal to current iamthif cb */ | |
637 | if (dev->iamthif_current_cb == cb_pos) { | |
638 | dev->iamthif_current_cb = NULL; | |
639 | /* send flow control to iamthif client */ | |
8120e720 TW |
640 | mei_hbm_cl_flow_control_req(dev, |
641 | &dev->iamthif_cl); | |
a562d5c2 TW |
642 | } |
643 | /* free all allocated buffers */ | |
644 | mei_io_cb_free(cb_pos); | |
645 | cb_pos = NULL; | |
646 | removed = true; | |
647 | } | |
648 | } | |
649 | return removed; | |
650 | } | |
651 | ||
652 | /** | |
653 | * mei_clear_lists - removes all callbacks associated with file | |
654 | * | |
655 | * @dev: device structure | |
656 | * @file: file structure | |
657 | * | |
658 | * mei_clear_lists is called to clear resources associated with file | |
659 | * when application calls close function or Ctrl-C was pressed | |
660 | * | |
a8605ea2 | 661 | * Return: true if callback removed from the list, false otherwise |
a562d5c2 TW |
662 | */ |
663 | static bool mei_clear_lists(struct mei_device *dev, struct file *file) | |
664 | { | |
665 | bool removed = false; | |
666 | ||
667 | /* remove callbacks associated with a file */ | |
668 | mei_clear_list(dev, file, &dev->amthif_cmd_list.list); | |
669 | if (mei_clear_list(dev, file, &dev->amthif_rd_complete_list.list)) | |
670 | removed = true; | |
19838fb8 | 671 | |
a562d5c2 TW |
672 | mei_clear_list(dev, file, &dev->ctrl_rd_list.list); |
673 | ||
674 | if (mei_clear_list(dev, file, &dev->ctrl_wr_list.list)) | |
675 | removed = true; | |
676 | ||
677 | if (mei_clear_list(dev, file, &dev->write_waiting_list.list)) | |
678 | removed = true; | |
679 | ||
680 | if (mei_clear_list(dev, file, &dev->write_list.list)) | |
681 | removed = true; | |
682 | ||
683 | /* check if iamthif_current_cb not NULL */ | |
684 | if (dev->iamthif_current_cb && !removed) { | |
685 | /* check file and iamthif current cb association */ | |
686 | if (dev->iamthif_current_cb->file_object == file) { | |
687 | /* remove cb */ | |
688 | mei_io_cb_free(dev->iamthif_current_cb); | |
689 | dev->iamthif_current_cb = NULL; | |
690 | removed = true; | |
691 | } | |
692 | } | |
693 | return removed; | |
694 | } | |
695 | ||
696 | /** | |
697 | * mei_amthif_release - the release function | |
698 | * | |
393b148f | 699 | * @dev: device structure |
a562d5c2 TW |
700 | * @file: pointer to file structure |
701 | * | |
a8605ea2 | 702 | * Return: 0 on success, <0 on error |
a562d5c2 TW |
703 | */ |
704 | int mei_amthif_release(struct mei_device *dev, struct file *file) | |
705 | { | |
22f96a0e TW |
706 | if (dev->iamthif_open_count > 0) |
707 | dev->iamthif_open_count--; | |
a562d5c2 TW |
708 | |
709 | if (dev->iamthif_file_object == file && | |
710 | dev->iamthif_state != MEI_IAMTHIF_IDLE) { | |
711 | ||
2bf94cab | 712 | dev_dbg(dev->dev, "amthif canceled iamthif state %d\n", |
a562d5c2 TW |
713 | dev->iamthif_state); |
714 | dev->iamthif_canceled = true; | |
715 | if (dev->iamthif_state == MEI_IAMTHIF_READ_COMPLETE) { | |
2bf94cab | 716 | dev_dbg(dev->dev, "run next amthif iamthif cb\n"); |
a562d5c2 TW |
717 | mei_amthif_run_next_cmd(dev); |
718 | } | |
719 | } | |
720 | ||
721 | if (mei_clear_lists(dev, file)) | |
722 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | |
723 | ||
724 | return 0; | |
725 | } |