diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-01 18:23:38 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-05-01 18:23:38 -0400 |
commit | ffc2825c2942b57c5dbfbcb3ad798696438aed62 (patch) | |
tree | 72be91daae4cbc84ba02152ccd7ae9899783a7d0 /drivers/misc/mei/iorw.c | |
parent | 589b3d06fd159774f9f5c3639d8d5d938670c019 (diff) |
Staging: mei: move the mei code out of staging
It's been cleaned up, and there's nothing else left to do, so move it
out of staging into drivers/misc/ where all can use it now.
Cc: Tomas Winkler <tomas.winkler@intel.com>
Cc: Oren Weil <oren.jer.weil@intel.com>
Cc: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei/iorw.c')
-rw-r--r-- | drivers/misc/mei/iorw.c | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/drivers/misc/mei/iorw.c b/drivers/misc/mei/iorw.c new file mode 100644 index 000000000000..0a80dc4e62f3 --- /dev/null +++ b/drivers/misc/mei/iorw.c | |||
@@ -0,0 +1,590 @@ | |||
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 | |||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/fcntl.h> | ||
23 | #include <linux/aio.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/ioctl.h> | ||
27 | #include <linux/cdev.h> | ||
28 | #include <linux/list.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/sched.h> | ||
31 | #include <linux/uuid.h> | ||
32 | #include <linux/jiffies.h> | ||
33 | #include <linux/uaccess.h> | ||
34 | |||
35 | |||
36 | #include "mei_dev.h" | ||
37 | #include "hw.h" | ||
38 | #include "mei.h" | ||
39 | #include "interface.h" | ||
40 | |||
41 | |||
42 | |||
43 | /** | ||
44 | * mei_ioctl_connect_client - the connect to fw client IOCTL function | ||
45 | * | ||
46 | * @dev: the device structure | ||
47 | * @data: IOCTL connect data, input and output parameters | ||
48 | * @file: private data of the file object | ||
49 | * | ||
50 | * Locking: called under "dev->device_lock" lock | ||
51 | * | ||
52 | * returns 0 on success, <0 on failure. | ||
53 | */ | ||
54 | int mei_ioctl_connect_client(struct file *file, | ||
55 | struct mei_connect_client_data *data) | ||
56 | { | ||
57 | struct mei_device *dev; | ||
58 | struct mei_cl_cb *cb; | ||
59 | struct mei_client *client; | ||
60 | struct mei_cl *cl; | ||
61 | struct mei_cl *cl_pos = NULL; | ||
62 | struct mei_cl *cl_next = NULL; | ||
63 | long timeout = CONNECT_TIMEOUT; | ||
64 | int i; | ||
65 | int err; | ||
66 | int rets; | ||
67 | |||
68 | cl = file->private_data; | ||
69 | if (WARN_ON(!cl || !cl->dev)) | ||
70 | return -ENODEV; | ||
71 | |||
72 | dev = cl->dev; | ||
73 | |||
74 | dev_dbg(&dev->pdev->dev, "mei_ioctl_connect_client() Entry\n"); | ||
75 | |||
76 | |||
77 | /* buffered ioctl cb */ | ||
78 | cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | ||
79 | if (!cb) { | ||
80 | rets = -ENOMEM; | ||
81 | goto end; | ||
82 | } | ||
83 | INIT_LIST_HEAD(&cb->cb_list); | ||
84 | |||
85 | cb->major_file_operations = MEI_IOCTL; | ||
86 | |||
87 | if (dev->mei_state != MEI_ENABLED) { | ||
88 | rets = -ENODEV; | ||
89 | goto end; | ||
90 | } | ||
91 | if (cl->state != MEI_FILE_INITIALIZING && | ||
92 | cl->state != MEI_FILE_DISCONNECTED) { | ||
93 | rets = -EBUSY; | ||
94 | goto end; | ||
95 | } | ||
96 | |||
97 | /* find ME client we're trying to connect to */ | ||
98 | i = mei_find_me_client_index(dev, data->in_client_uuid); | ||
99 | if (i >= 0 && !dev->me_clients[i].props.fixed_address) { | ||
100 | cl->me_client_id = dev->me_clients[i].client_id; | ||
101 | cl->state = MEI_FILE_CONNECTING; | ||
102 | } | ||
103 | |||
104 | dev_dbg(&dev->pdev->dev, "Connect to FW Client ID = %d\n", | ||
105 | cl->me_client_id); | ||
106 | dev_dbg(&dev->pdev->dev, "FW Client - Protocol Version = %d\n", | ||
107 | dev->me_clients[i].props.protocol_version); | ||
108 | dev_dbg(&dev->pdev->dev, "FW Client - Max Msg Len = %d\n", | ||
109 | dev->me_clients[i].props.max_msg_length); | ||
110 | |||
111 | /* if we're connecting to amthi client then we will use the | ||
112 | * existing connection | ||
113 | */ | ||
114 | if (uuid_le_cmp(data->in_client_uuid, mei_amthi_guid) == 0) { | ||
115 | dev_dbg(&dev->pdev->dev, "FW Client is amthi\n"); | ||
116 | if (dev->iamthif_cl.state != MEI_FILE_CONNECTED) { | ||
117 | rets = -ENODEV; | ||
118 | goto end; | ||
119 | } | ||
120 | clear_bit(cl->host_client_id, dev->host_clients_map); | ||
121 | list_for_each_entry_safe(cl_pos, cl_next, | ||
122 | &dev->file_list, link) { | ||
123 | if (mei_cl_cmp_id(cl, cl_pos)) { | ||
124 | dev_dbg(&dev->pdev->dev, | ||
125 | "remove file private data node host" | ||
126 | " client = %d, ME client = %d.\n", | ||
127 | cl_pos->host_client_id, | ||
128 | cl_pos->me_client_id); | ||
129 | list_del(&cl_pos->link); | ||
130 | } | ||
131 | |||
132 | } | ||
133 | dev_dbg(&dev->pdev->dev, "free file private data memory.\n"); | ||
134 | kfree(cl); | ||
135 | |||
136 | cl = NULL; | ||
137 | file->private_data = &dev->iamthif_cl; | ||
138 | |||
139 | client = &data->out_client_properties; | ||
140 | client->max_msg_length = | ||
141 | dev->me_clients[i].props.max_msg_length; | ||
142 | client->protocol_version = | ||
143 | dev->me_clients[i].props.protocol_version; | ||
144 | rets = dev->iamthif_cl.status; | ||
145 | |||
146 | goto end; | ||
147 | } | ||
148 | |||
149 | if (cl->state != MEI_FILE_CONNECTING) { | ||
150 | rets = -ENODEV; | ||
151 | goto end; | ||
152 | } | ||
153 | |||
154 | |||
155 | /* prepare the output buffer */ | ||
156 | client = &data->out_client_properties; | ||
157 | client->max_msg_length = dev->me_clients[i].props.max_msg_length; | ||
158 | client->protocol_version = dev->me_clients[i].props.protocol_version; | ||
159 | dev_dbg(&dev->pdev->dev, "Can connect?\n"); | ||
160 | if (dev->mei_host_buffer_is_empty | ||
161 | && !mei_other_client_is_connecting(dev, cl)) { | ||
162 | dev_dbg(&dev->pdev->dev, "Sending Connect Message\n"); | ||
163 | dev->mei_host_buffer_is_empty = false; | ||
164 | if (mei_connect(dev, cl)) { | ||
165 | dev_dbg(&dev->pdev->dev, "Sending connect message - failed\n"); | ||
166 | rets = -ENODEV; | ||
167 | goto end; | ||
168 | } else { | ||
169 | dev_dbg(&dev->pdev->dev, "Sending connect message - succeeded\n"); | ||
170 | cl->timer_count = MEI_CONNECT_TIMEOUT; | ||
171 | cb->file_private = cl; | ||
172 | list_add_tail(&cb->cb_list, | ||
173 | &dev->ctrl_rd_list.mei_cb. | ||
174 | cb_list); | ||
175 | } | ||
176 | |||
177 | |||
178 | } else { | ||
179 | dev_dbg(&dev->pdev->dev, "Queuing the connect request due to device busy\n"); | ||
180 | cb->file_private = cl; | ||
181 | dev_dbg(&dev->pdev->dev, "add connect cb to control write list.\n"); | ||
182 | list_add_tail(&cb->cb_list, | ||
183 | &dev->ctrl_wr_list.mei_cb.cb_list); | ||
184 | } | ||
185 | mutex_unlock(&dev->device_lock); | ||
186 | err = wait_event_timeout(dev->wait_recvd_msg, | ||
187 | (MEI_FILE_CONNECTED == cl->state || | ||
188 | MEI_FILE_DISCONNECTED == cl->state), | ||
189 | timeout * HZ); | ||
190 | |||
191 | mutex_lock(&dev->device_lock); | ||
192 | if (MEI_FILE_CONNECTED == cl->state) { | ||
193 | dev_dbg(&dev->pdev->dev, "successfully connected to FW client.\n"); | ||
194 | rets = cl->status; | ||
195 | goto end; | ||
196 | } else { | ||
197 | dev_dbg(&dev->pdev->dev, "failed to connect to FW client.cl->state = %d.\n", | ||
198 | cl->state); | ||
199 | if (!err) { | ||
200 | dev_dbg(&dev->pdev->dev, | ||
201 | "wait_event_interruptible_timeout failed on client" | ||
202 | " connect message fw response message.\n"); | ||
203 | } | ||
204 | rets = -EFAULT; | ||
205 | |||
206 | mei_io_list_flush(&dev->ctrl_rd_list, cl); | ||
207 | mei_io_list_flush(&dev->ctrl_wr_list, cl); | ||
208 | goto end; | ||
209 | } | ||
210 | rets = 0; | ||
211 | end: | ||
212 | dev_dbg(&dev->pdev->dev, "free connect cb memory."); | ||
213 | kfree(cb); | ||
214 | return rets; | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * find_amthi_read_list_entry - finds a amthilist entry for current file | ||
219 | * | ||
220 | * @dev: the device structure | ||
221 | * @file: pointer to file object | ||
222 | * | ||
223 | * returns returned a list entry on success, NULL on failure. | ||
224 | */ | ||
225 | struct mei_cl_cb *find_amthi_read_list_entry( | ||
226 | struct mei_device *dev, | ||
227 | struct file *file) | ||
228 | { | ||
229 | struct mei_cl *cl_temp; | ||
230 | struct mei_cl_cb *pos = NULL; | ||
231 | struct mei_cl_cb *next = NULL; | ||
232 | |||
233 | list_for_each_entry_safe(pos, next, | ||
234 | &dev->amthi_read_complete_list.mei_cb.cb_list, cb_list) { | ||
235 | cl_temp = (struct mei_cl *)pos->file_private; | ||
236 | if (cl_temp && cl_temp == &dev->iamthif_cl && | ||
237 | pos->file_object == file) | ||
238 | return pos; | ||
239 | } | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | /** | ||
244 | * amthi_read - read data from AMTHI client | ||
245 | * | ||
246 | * @dev: the device structure | ||
247 | * @if_num: minor number | ||
248 | * @file: pointer to file object | ||
249 | * @*ubuf: pointer to user data in user space | ||
250 | * @length: data length to read | ||
251 | * @offset: data read offset | ||
252 | * | ||
253 | * Locking: called under "dev->device_lock" lock | ||
254 | * | ||
255 | * returns | ||
256 | * returned data length on success, | ||
257 | * zero if no data to read, | ||
258 | * negative on failure. | ||
259 | */ | ||
260 | int amthi_read(struct mei_device *dev, struct file *file, | ||
261 | char __user *ubuf, size_t length, loff_t *offset) | ||
262 | { | ||
263 | int rets; | ||
264 | int wait_ret; | ||
265 | struct mei_cl_cb *cb = NULL; | ||
266 | struct mei_cl *cl = file->private_data; | ||
267 | unsigned long timeout; | ||
268 | int i; | ||
269 | |||
270 | /* Only Posible if we are in timeout */ | ||
271 | if (!cl || cl != &dev->iamthif_cl) { | ||
272 | dev_dbg(&dev->pdev->dev, "bad file ext.\n"); | ||
273 | return -ETIMEDOUT; | ||
274 | } | ||
275 | |||
276 | for (i = 0; i < dev->me_clients_num; i++) { | ||
277 | if (dev->me_clients[i].client_id == | ||
278 | dev->iamthif_cl.me_client_id) | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | if (i == dev->me_clients_num) { | ||
283 | dev_dbg(&dev->pdev->dev, "amthi client not found.\n"); | ||
284 | return -ENODEV; | ||
285 | } | ||
286 | if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) | ||
287 | return -ENODEV; | ||
288 | |||
289 | dev_dbg(&dev->pdev->dev, "checking amthi data\n"); | ||
290 | cb = find_amthi_read_list_entry(dev, file); | ||
291 | |||
292 | /* Check for if we can block or not*/ | ||
293 | if (cb == NULL && file->f_flags & O_NONBLOCK) | ||
294 | return -EAGAIN; | ||
295 | |||
296 | |||
297 | dev_dbg(&dev->pdev->dev, "waiting for amthi data\n"); | ||
298 | while (cb == NULL) { | ||
299 | /* unlock the Mutex */ | ||
300 | mutex_unlock(&dev->device_lock); | ||
301 | |||
302 | wait_ret = wait_event_interruptible(dev->iamthif_cl.wait, | ||
303 | (cb = find_amthi_read_list_entry(dev, file))); | ||
304 | |||
305 | if (wait_ret) | ||
306 | return -ERESTARTSYS; | ||
307 | |||
308 | dev_dbg(&dev->pdev->dev, "woke up from sleep\n"); | ||
309 | |||
310 | /* Locking again the Mutex */ | ||
311 | mutex_lock(&dev->device_lock); | ||
312 | } | ||
313 | |||
314 | |||
315 | dev_dbg(&dev->pdev->dev, "Got amthi data\n"); | ||
316 | dev->iamthif_timer = 0; | ||
317 | |||
318 | if (cb) { | ||
319 | timeout = cb->read_time + | ||
320 | msecs_to_jiffies(IAMTHIF_READ_TIMER); | ||
321 | dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n", | ||
322 | timeout); | ||
323 | |||
324 | if (time_after(jiffies, timeout)) { | ||
325 | dev_dbg(&dev->pdev->dev, "amthi Time out\n"); | ||
326 | /* 15 sec for the message has expired */ | ||
327 | list_del(&cb->cb_list); | ||
328 | rets = -ETIMEDOUT; | ||
329 | goto free; | ||
330 | } | ||
331 | } | ||
332 | /* if the whole message will fit remove it from the list */ | ||
333 | if (cb->information >= *offset && length >= (cb->information - *offset)) | ||
334 | list_del(&cb->cb_list); | ||
335 | else if (cb->information > 0 && cb->information <= *offset) { | ||
336 | /* end of the message has been reached */ | ||
337 | list_del(&cb->cb_list); | ||
338 | rets = 0; | ||
339 | goto free; | ||
340 | } | ||
341 | /* else means that not full buffer will be read and do not | ||
342 | * remove message from deletion list | ||
343 | */ | ||
344 | |||
345 | dev_dbg(&dev->pdev->dev, "amthi cb->response_buffer size - %d\n", | ||
346 | cb->response_buffer.size); | ||
347 | dev_dbg(&dev->pdev->dev, "amthi cb->information - %lu\n", | ||
348 | cb->information); | ||
349 | |||
350 | /* length is being turncated to PAGE_SIZE, however, | ||
351 | * the information may be longer */ | ||
352 | length = min_t(size_t, length, (cb->information - *offset)); | ||
353 | |||
354 | if (copy_to_user(ubuf, cb->response_buffer.data + *offset, length)) | ||
355 | rets = -EFAULT; | ||
356 | else { | ||
357 | rets = length; | ||
358 | if ((*offset + length) < cb->information) { | ||
359 | *offset += length; | ||
360 | goto out; | ||
361 | } | ||
362 | } | ||
363 | free: | ||
364 | dev_dbg(&dev->pdev->dev, "free amthi cb memory.\n"); | ||
365 | *offset = 0; | ||
366 | mei_free_cb_private(cb); | ||
367 | out: | ||
368 | return rets; | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * mei_start_read - the start read client message function. | ||
373 | * | ||
374 | * @dev: the device structure | ||
375 | * @if_num: minor number | ||
376 | * @cl: private data of the file object | ||
377 | * | ||
378 | * returns 0 on success, <0 on failure. | ||
379 | */ | ||
380 | int mei_start_read(struct mei_device *dev, struct mei_cl *cl) | ||
381 | { | ||
382 | struct mei_cl_cb *cb; | ||
383 | int rets = 0; | ||
384 | int i; | ||
385 | |||
386 | if (cl->state != MEI_FILE_CONNECTED) | ||
387 | return -ENODEV; | ||
388 | |||
389 | if (dev->mei_state != MEI_ENABLED) | ||
390 | return -ENODEV; | ||
391 | |||
392 | dev_dbg(&dev->pdev->dev, "check if read is pending.\n"); | ||
393 | if (cl->read_pending || cl->read_cb) { | ||
394 | dev_dbg(&dev->pdev->dev, "read is pending.\n"); | ||
395 | return -EBUSY; | ||
396 | } | ||
397 | |||
398 | cb = kzalloc(sizeof(struct mei_cl_cb), GFP_KERNEL); | ||
399 | if (!cb) | ||
400 | return -ENOMEM; | ||
401 | |||
402 | dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n", | ||
403 | cl->host_client_id, cl->me_client_id); | ||
404 | |||
405 | for (i = 0; i < dev->me_clients_num; i++) { | ||
406 | if (dev->me_clients[i].client_id == cl->me_client_id) | ||
407 | break; | ||
408 | |||
409 | } | ||
410 | |||
411 | if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) { | ||
412 | rets = -ENODEV; | ||
413 | goto unlock; | ||
414 | } | ||
415 | |||
416 | if (i == dev->me_clients_num) { | ||
417 | rets = -ENODEV; | ||
418 | goto unlock; | ||
419 | } | ||
420 | |||
421 | cb->response_buffer.size = dev->me_clients[i].props.max_msg_length; | ||
422 | cb->response_buffer.data = | ||
423 | kmalloc(cb->response_buffer.size, GFP_KERNEL); | ||
424 | if (!cb->response_buffer.data) { | ||
425 | rets = -ENOMEM; | ||
426 | goto unlock; | ||
427 | } | ||
428 | dev_dbg(&dev->pdev->dev, "allocation call back data success.\n"); | ||
429 | cb->major_file_operations = MEI_READ; | ||
430 | /* make sure information is zero before we start */ | ||
431 | cb->information = 0; | ||
432 | cb->file_private = (void *) cl; | ||
433 | cl->read_cb = cb; | ||
434 | if (dev->mei_host_buffer_is_empty) { | ||
435 | dev->mei_host_buffer_is_empty = false; | ||
436 | if (mei_send_flow_control(dev, cl)) { | ||
437 | rets = -ENODEV; | ||
438 | goto unlock; | ||
439 | } | ||
440 | list_add_tail(&cb->cb_list, &dev->read_list.mei_cb.cb_list); | ||
441 | } else { | ||
442 | list_add_tail(&cb->cb_list, &dev->ctrl_wr_list.mei_cb.cb_list); | ||
443 | } | ||
444 | return rets; | ||
445 | unlock: | ||
446 | mei_free_cb_private(cb); | ||
447 | return rets; | ||
448 | } | ||
449 | |||
450 | /** | ||
451 | * amthi_write - write iamthif data to amthi client | ||
452 | * | ||
453 | * @dev: the device structure | ||
454 | * @cb: mei call back struct | ||
455 | * | ||
456 | * returns 0 on success, <0 on failure. | ||
457 | */ | ||
458 | int amthi_write(struct mei_device *dev, struct mei_cl_cb *cb) | ||
459 | { | ||
460 | struct mei_msg_hdr mei_hdr; | ||
461 | int ret; | ||
462 | |||
463 | if (!dev || !cb) | ||
464 | return -ENODEV; | ||
465 | |||
466 | dev_dbg(&dev->pdev->dev, "write data to amthi client.\n"); | ||
467 | |||
468 | dev->iamthif_state = MEI_IAMTHIF_WRITING; | ||
469 | dev->iamthif_current_cb = cb; | ||
470 | dev->iamthif_file_object = cb->file_object; | ||
471 | dev->iamthif_canceled = false; | ||
472 | dev->iamthif_ioctl = true; | ||
473 | dev->iamthif_msg_buf_size = cb->request_buffer.size; | ||
474 | memcpy(dev->iamthif_msg_buf, cb->request_buffer.data, | ||
475 | cb->request_buffer.size); | ||
476 | |||
477 | ret = mei_flow_ctrl_creds(dev, &dev->iamthif_cl); | ||
478 | if (ret < 0) | ||
479 | return ret; | ||
480 | |||
481 | if (ret && dev->mei_host_buffer_is_empty) { | ||
482 | ret = 0; | ||
483 | dev->mei_host_buffer_is_empty = false; | ||
484 | if (cb->request_buffer.size > | ||
485 | (((dev->host_hw_state & H_CBD) >> 24) * sizeof(u32)) | ||
486 | -sizeof(struct mei_msg_hdr)) { | ||
487 | mei_hdr.length = | ||
488 | (((dev->host_hw_state & H_CBD) >> 24) * | ||
489 | sizeof(u32)) - sizeof(struct mei_msg_hdr); | ||
490 | mei_hdr.msg_complete = 0; | ||
491 | } else { | ||
492 | mei_hdr.length = cb->request_buffer.size; | ||
493 | mei_hdr.msg_complete = 1; | ||
494 | } | ||
495 | |||
496 | mei_hdr.host_addr = dev->iamthif_cl.host_client_id; | ||
497 | mei_hdr.me_addr = dev->iamthif_cl.me_client_id; | ||
498 | mei_hdr.reserved = 0; | ||
499 | dev->iamthif_msg_buf_index += mei_hdr.length; | ||
500 | if (mei_write_message(dev, &mei_hdr, | ||
501 | (unsigned char *)(dev->iamthif_msg_buf), | ||
502 | mei_hdr.length)) | ||
503 | return -ENODEV; | ||
504 | |||
505 | if (mei_hdr.msg_complete) { | ||
506 | if (mei_flow_ctrl_reduce(dev, &dev->iamthif_cl)) | ||
507 | return -ENODEV; | ||
508 | dev->iamthif_flow_control_pending = true; | ||
509 | dev->iamthif_state = MEI_IAMTHIF_FLOW_CONTROL; | ||
510 | dev_dbg(&dev->pdev->dev, "add amthi cb to write waiting list\n"); | ||
511 | dev->iamthif_current_cb = cb; | ||
512 | dev->iamthif_file_object = cb->file_object; | ||
513 | list_add_tail(&cb->cb_list, | ||
514 | &dev->write_waiting_list.mei_cb.cb_list); | ||
515 | } else { | ||
516 | dev_dbg(&dev->pdev->dev, "message does not complete, " | ||
517 | "so add amthi cb to write list.\n"); | ||
518 | list_add_tail(&cb->cb_list, | ||
519 | &dev->write_list.mei_cb.cb_list); | ||
520 | } | ||
521 | } else { | ||
522 | if (!(dev->mei_host_buffer_is_empty)) | ||
523 | dev_dbg(&dev->pdev->dev, "host buffer is not empty"); | ||
524 | |||
525 | dev_dbg(&dev->pdev->dev, "No flow control credentials, " | ||
526 | "so add iamthif cb to write list.\n"); | ||
527 | list_add_tail(&cb->cb_list, &dev->write_list.mei_cb.cb_list); | ||
528 | } | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | /** | ||
533 | * iamthif_ioctl_send_msg - send cmd data to amthi client | ||
534 | * | ||
535 | * @dev: the device structure | ||
536 | * | ||
537 | * returns 0 on success, <0 on failure. | ||
538 | */ | ||
539 | void mei_run_next_iamthif_cmd(struct mei_device *dev) | ||
540 | { | ||
541 | struct mei_cl *cl_tmp; | ||
542 | struct mei_cl_cb *pos = NULL; | ||
543 | struct mei_cl_cb *next = NULL; | ||
544 | int status; | ||
545 | |||
546 | if (!dev) | ||
547 | return; | ||
548 | |||
549 | dev->iamthif_msg_buf_size = 0; | ||
550 | dev->iamthif_msg_buf_index = 0; | ||
551 | dev->iamthif_canceled = false; | ||
552 | dev->iamthif_ioctl = true; | ||
553 | dev->iamthif_state = MEI_IAMTHIF_IDLE; | ||
554 | dev->iamthif_timer = 0; | ||
555 | dev->iamthif_file_object = NULL; | ||
556 | |||
557 | dev_dbg(&dev->pdev->dev, "complete amthi cmd_list cb.\n"); | ||
558 | |||
559 | list_for_each_entry_safe(pos, next, | ||
560 | &dev->amthi_cmd_list.mei_cb.cb_list, cb_list) { | ||
561 | list_del(&pos->cb_list); | ||
562 | cl_tmp = (struct mei_cl *)pos->file_private; | ||
563 | |||
564 | if (cl_tmp && cl_tmp == &dev->iamthif_cl) { | ||
565 | status = amthi_write(dev, pos); | ||
566 | if (status) { | ||
567 | dev_dbg(&dev->pdev->dev, | ||
568 | "amthi write failed status = %d\n", | ||
569 | status); | ||
570 | return; | ||
571 | } | ||
572 | break; | ||
573 | } | ||
574 | } | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * mei_free_cb_private - free mei_cb_private related memory | ||
579 | * | ||
580 | * @cb: mei callback struct | ||
581 | */ | ||
582 | void mei_free_cb_private(struct mei_cl_cb *cb) | ||
583 | { | ||
584 | if (cb == NULL) | ||
585 | return; | ||
586 | |||
587 | kfree(cb->request_buffer.data); | ||
588 | kfree(cb->response_buffer.data); | ||
589 | kfree(cb); | ||
590 | } | ||