Commit | Line | Data |
---|---|---|
d52b3d9c MO |
1 | /* |
2 | * Part of Intel(R) Manageability Engine Interface Linux driver | |
3 | * | |
4 | * Copyright (c) 2003 - 2008 Intel Corp. | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * 1. Redistributions of source code must retain the above copyright | |
11 | * notice, this list of conditions, and the following disclaimer, | |
12 | * without modification. | |
13 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
14 | * substantially similar to the "NO WARRANTY" disclaimer below | |
15 | * ("Disclaimer") and any redistribution must be conditioned upon | |
16 | * including a substantially similar Disclaimer requirement for further | |
17 | * binary redistribution. | |
18 | * 3. Neither the names of the above-listed copyright holders nor the names | |
19 | * of any contributors may be used to endorse or promote products derived | |
20 | * from this software without specific prior written permission. | |
21 | * | |
22 | * Alternatively, this software may be distributed under the terms of the | |
23 | * GNU General Public License ("GPL") version 2 as published by the Free | |
24 | * Software Foundation. | |
25 | * | |
26 | * NO WARRANTY | |
27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
28 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
29 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |
30 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
31 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
35 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
36 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
37 | * POSSIBILITY OF SUCH DAMAGES. | |
38 | * | |
39 | */ | |
40 | ||
41 | #include <linux/module.h> | |
42 | #include <linux/moduleparam.h> | |
43 | #include <linux/kernel.h> | |
44 | #include <linux/slab.h> | |
45 | #include <linux/fs.h> | |
46 | #include <linux/errno.h> | |
47 | #include <linux/types.h> | |
48 | #include <linux/fcntl.h> | |
49 | #include <linux/aio.h> | |
50 | #include <linux/pci.h> | |
51 | #include <linux/reboot.h> | |
52 | #include <linux/poll.h> | |
53 | #include <linux/init.h> | |
54 | #include <linux/kdev_t.h> | |
55 | #include <linux/ioctl.h> | |
56 | #include <linux/cdev.h> | |
57 | #include <linux/list.h> | |
58 | #include <linux/unistd.h> | |
59 | #include <linux/delay.h> | |
d52b3d9c MO |
60 | |
61 | #include "heci_data_structures.h" | |
62 | #include "heci.h" | |
63 | #include "heci_interface.h" | |
64 | #include "heci_version.h" | |
65 | ||
66 | ||
67 | /** | |
68 | * heci_ioctl_get_version - the get driver version IOCTL function | |
69 | * | |
70 | * @dev: Device object for our driver | |
71 | * @if_num: minor number | |
72 | * @*u_msg: pointer to user data struct in user space | |
73 | * @k_msg: data in kernel on the stack | |
74 | * @file_ext: private data of the file object | |
75 | * | |
76 | * returns 0 on success, <0 on failure. | |
77 | */ | |
78 | int heci_ioctl_get_version(struct iamt_heci_device *dev, int if_num, | |
c4739ea6 | 79 | struct heci_message_data __user *u_msg, |
d52b3d9c MO |
80 | struct heci_message_data k_msg, |
81 | struct heci_file_private *file_ext) | |
82 | { | |
83 | int rets = 0; | |
84 | struct heci_driver_version *version; | |
85 | struct heci_message_data res_msg; | |
86 | ||
87 | if ((if_num != HECI_MINOR_NUMBER) || (!dev) | |
88 | || (!file_ext)) | |
89 | return -ENODEV; | |
90 | ||
91 | if (k_msg.size < (sizeof(struct heci_driver_version) - 2)) { | |
92 | DBG("user buffer less than heci_driver_version.\n"); | |
93 | return -EMSGSIZE; | |
94 | } | |
95 | ||
96 | res_msg.data = kmalloc(sizeof(struct heci_driver_version), GFP_KERNEL); | |
97 | if (!res_msg.data) { | |
98 | DBG("failed allocation response buffer size = %d.\n", | |
99 | (int) sizeof(struct heci_driver_version)); | |
100 | return -ENOMEM; | |
101 | } | |
102 | ||
103 | version = (struct heci_driver_version *) res_msg.data; | |
104 | version->major = MAJOR_VERSION; | |
105 | version->minor = MINOR_VERSION; | |
106 | version->hotfix = QUICK_FIX_NUMBER; | |
107 | version->build = VER_BUILD; | |
108 | res_msg.size = sizeof(struct heci_driver_version); | |
109 | if (k_msg.size < sizeof(struct heci_driver_version)) | |
110 | res_msg.size -= 2; | |
111 | ||
112 | rets = file_ext->status; | |
113 | /* now copy the data to user space */ | |
36e84467 | 114 | if (copy_to_user((void __user *)k_msg.data, res_msg.data, res_msg.size)) { |
d52b3d9c MO |
115 | rets = -EFAULT; |
116 | goto end; | |
117 | } | |
118 | if (put_user(res_msg.size, &u_msg->size)) { | |
119 | rets = -EFAULT; | |
120 | goto end; | |
121 | } | |
122 | end: | |
123 | kfree(res_msg.data); | |
124 | return rets; | |
125 | } | |
126 | ||
127 | /** | |
128 | * heci_ioctl_connect_client - the connect to fw client IOCTL function | |
129 | * | |
130 | * @dev: Device object for our driver | |
131 | * @if_num: minor number | |
132 | * @*u_msg: pointer to user data struct in user space | |
133 | * @k_msg: data in kernel on the stack | |
134 | * @file_ext: private data of the file object | |
135 | * | |
136 | * returns 0 on success, <0 on failure. | |
137 | */ | |
138 | int heci_ioctl_connect_client(struct iamt_heci_device *dev, int if_num, | |
c4739ea6 | 139 | struct heci_message_data __user *u_msg, |
d52b3d9c MO |
140 | struct heci_message_data k_msg, |
141 | struct file *file) | |
142 | { | |
143 | int rets = 0; | |
144 | struct heci_message_data req_msg, res_msg; | |
145 | struct heci_cb_private *priv_cb = NULL; | |
146 | struct heci_client *client; | |
147 | struct heci_file_private *file_ext; | |
148 | struct heci_file_private *file_pos = NULL; | |
149 | struct heci_file_private *file_next = NULL; | |
150 | long timeout = 15; /*15 second */ | |
151 | __u8 i; | |
152 | int err = 0; | |
153 | ||
154 | if ((if_num != HECI_MINOR_NUMBER) || (!dev) || (!file)) | |
155 | return -ENODEV; | |
156 | ||
157 | file_ext = file->private_data; | |
158 | if (!file_ext) | |
159 | return -ENODEV; | |
160 | ||
161 | if (k_msg.size != sizeof(struct guid)) { | |
162 | DBG("user buffer size is not equal to size of struct " | |
163 | "guid(16).\n"); | |
164 | return -EMSGSIZE; | |
165 | } | |
166 | ||
167 | if (!k_msg.data) | |
168 | return -EIO; | |
169 | ||
170 | req_msg.data = kmalloc(sizeof(struct guid), GFP_KERNEL); | |
171 | res_msg.data = kmalloc(sizeof(struct heci_client), GFP_KERNEL); | |
172 | ||
173 | if (!res_msg.data) { | |
174 | DBG("failed allocation response buffer size = %d.\n", | |
175 | (int) sizeof(struct heci_client)); | |
176 | kfree(req_msg.data); | |
177 | return -ENOMEM; | |
178 | } | |
179 | if (!req_msg.data) { | |
180 | DBG("failed allocation request buffer size = %d.\n", | |
181 | (int) sizeof(struct guid)); | |
182 | kfree(res_msg.data); | |
183 | return -ENOMEM; | |
184 | } | |
185 | req_msg.size = sizeof(struct guid); | |
186 | res_msg.size = sizeof(struct heci_client); | |
187 | ||
188 | /* copy the message to kernel space - | |
189 | * use a pointer already copied into kernel space | |
190 | */ | |
36e84467 | 191 | if (copy_from_user(req_msg.data, (void __user *)k_msg.data, k_msg.size)) { |
d52b3d9c MO |
192 | rets = -EFAULT; |
193 | goto end; | |
194 | } | |
195 | /* buffered ioctl cb */ | |
196 | priv_cb = kzalloc(sizeof(struct heci_cb_private), GFP_KERNEL); | |
197 | if (!priv_cb) { | |
198 | rets = -ENOMEM; | |
199 | goto end; | |
200 | } | |
201 | INIT_LIST_HEAD(&priv_cb->cb_list); | |
202 | priv_cb->response_buffer.data = res_msg.data; | |
203 | priv_cb->response_buffer.size = res_msg.size; | |
204 | priv_cb->request_buffer.data = req_msg.data; | |
205 | priv_cb->request_buffer.size = req_msg.size; | |
206 | priv_cb->major_file_operations = HECI_IOCTL; | |
207 | spin_lock_bh(&dev->device_lock); | |
208 | if (dev->heci_state != HECI_ENABLED) { | |
209 | rets = -ENODEV; | |
210 | spin_unlock_bh(&dev->device_lock); | |
211 | goto end; | |
212 | } | |
213 | if ((file_ext->state != HECI_FILE_INITIALIZING) && | |
214 | (file_ext->state != HECI_FILE_DISCONNECTED)) { | |
215 | rets = -EBUSY; | |
216 | spin_unlock_bh(&dev->device_lock); | |
217 | goto end; | |
218 | } | |
219 | ||
220 | /* find ME client we're trying to connect to */ | |
221 | for (i = 0; i < dev->num_heci_me_clients; i++) { | |
222 | if (memcmp((struct guid *)req_msg.data, | |
223 | &dev->me_clients[i].props.protocol_name, | |
224 | sizeof(struct guid)) == 0) { | |
225 | if (dev->me_clients[i].props.fixed_address == 0) { | |
226 | file_ext->me_client_id = | |
227 | dev->me_clients[i].client_id; | |
228 | file_ext->state = HECI_FILE_CONNECTING; | |
229 | } | |
230 | break; | |
231 | } | |
232 | } | |
233 | /* if we're connecting to PTHI client so we will use the exist | |
234 | * connection | |
235 | */ | |
236 | if (memcmp((struct guid *)req_msg.data, &heci_pthi_guid, | |
237 | sizeof(struct guid)) == 0) { | |
238 | if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) { | |
239 | rets = -ENODEV; | |
240 | spin_unlock_bh(&dev->device_lock); | |
241 | goto end; | |
242 | } | |
243 | dev->heci_host_clients[file_ext->host_client_id / 8] &= | |
244 | ~(1 << (file_ext->host_client_id % 8)); | |
245 | list_for_each_entry_safe(file_pos, | |
246 | file_next, &dev->file_list, link) { | |
247 | if (heci_fe_same_id(file_ext, file_pos)) { | |
248 | DBG("remove file private data node host" | |
249 | " client = %d, ME client = %d.\n", | |
250 | file_pos->host_client_id, | |
251 | file_pos->me_client_id); | |
252 | list_del(&file_pos->link); | |
253 | } | |
254 | ||
255 | } | |
256 | DBG("free file private data memory.\n"); | |
257 | kfree(file_ext); | |
258 | file_ext = NULL; | |
259 | file->private_data = &dev->iamthif_file_ext; | |
260 | client = (struct heci_client *) res_msg.data; | |
261 | client->max_msg_length = | |
262 | dev->me_clients[i].props.max_msg_length; | |
263 | client->protocol_version = | |
264 | dev->me_clients[i].props.protocol_version; | |
265 | rets = dev->iamthif_file_ext.status; | |
266 | spin_unlock_bh(&dev->device_lock); | |
267 | ||
268 | /* now copy the data to user space */ | |
36e84467 DX |
269 | if (copy_to_user((void __user *)k_msg.data, |
270 | res_msg.data, res_msg.size)) { | |
d52b3d9c MO |
271 | rets = -EFAULT; |
272 | goto end; | |
273 | } | |
274 | if (put_user(res_msg.size, &u_msg->size)) { | |
275 | rets = -EFAULT; | |
276 | goto end; | |
277 | } | |
278 | goto end; | |
279 | } | |
171df638 DX |
280 | spin_unlock_bh(&dev->device_lock); |
281 | ||
d52b3d9c | 282 | spin_lock(&file_ext->file_lock); |
171df638 | 283 | spin_lock_bh(&dev->device_lock); |
d52b3d9c MO |
284 | if (file_ext->state != HECI_FILE_CONNECTING) { |
285 | rets = -ENODEV; | |
d52b3d9c | 286 | spin_unlock_bh(&dev->device_lock); |
171df638 | 287 | spin_unlock(&file_ext->file_lock); |
d52b3d9c MO |
288 | goto end; |
289 | } | |
d52b3d9c MO |
290 | /* prepare the output buffer */ |
291 | client = (struct heci_client *) res_msg.data; | |
292 | client->max_msg_length = dev->me_clients[i].props.max_msg_length; | |
293 | client->protocol_version = dev->me_clients[i].props.protocol_version; | |
294 | if (dev->host_buffer_is_empty | |
295 | && !other_client_is_connecting(dev, file_ext)) { | |
296 | dev->host_buffer_is_empty = 0; | |
297 | if (!heci_connect(dev, file_ext)) { | |
298 | rets = -ENODEV; | |
299 | spin_unlock_bh(&dev->device_lock); | |
afcf462a | 300 | spin_unlock(&file_ext->file_lock); |
d52b3d9c MO |
301 | goto end; |
302 | } else { | |
303 | file_ext->timer_count = HECI_CONNECT_TIMEOUT; | |
304 | priv_cb->file_private = file_ext; | |
305 | list_add_tail(&priv_cb->cb_list, | |
306 | &dev->ctrl_rd_list.heci_cb. | |
307 | cb_list); | |
308 | } | |
309 | ||
310 | ||
311 | } else { | |
312 | priv_cb->file_private = file_ext; | |
313 | DBG("add connect cb to control write list.\n"); | |
314 | list_add_tail(&priv_cb->cb_list, | |
315 | &dev->ctrl_wr_list.heci_cb.cb_list); | |
316 | } | |
317 | spin_unlock_bh(&dev->device_lock); | |
171df638 | 318 | spin_unlock(&file_ext->file_lock); |
d52b3d9c MO |
319 | err = wait_event_timeout(dev->wait_recvd_msg, |
320 | (HECI_FILE_CONNECTED == file_ext->state | |
321 | || HECI_FILE_DISCONNECTED == file_ext->state), | |
322 | timeout * HZ); | |
323 | ||
afcf462a | 324 | spin_lock_bh(&dev->device_lock); |
d52b3d9c | 325 | if (HECI_FILE_CONNECTED == file_ext->state) { |
afcf462a | 326 | spin_unlock_bh(&dev->device_lock); |
d52b3d9c MO |
327 | DBG("successfully connected to FW client.\n"); |
328 | rets = file_ext->status; | |
329 | /* now copy the data to user space */ | |
36e84467 DX |
330 | if (copy_to_user((void __user *)k_msg.data, |
331 | res_msg.data, res_msg.size)) { | |
d52b3d9c MO |
332 | rets = -EFAULT; |
333 | goto end; | |
334 | } | |
335 | if (put_user(res_msg.size, &u_msg->size)) { | |
336 | rets = -EFAULT; | |
337 | goto end; | |
338 | } | |
339 | goto end; | |
340 | } else { | |
341 | DBG("failed to connect to FW client.file_ext->state = %d.\n", | |
342 | file_ext->state); | |
afcf462a | 343 | spin_unlock_bh(&dev->device_lock); |
d52b3d9c MO |
344 | if (!err) { |
345 | DBG("wait_event_interruptible_timeout failed on client" | |
346 | " connect message fw response message.\n"); | |
347 | } | |
348 | rets = -EFAULT; | |
349 | goto remove_list; | |
350 | } | |
351 | ||
352 | remove_list: | |
353 | if (priv_cb) { | |
354 | spin_lock_bh(&dev->device_lock); | |
355 | heci_flush_list(&dev->ctrl_rd_list, file_ext); | |
356 | heci_flush_list(&dev->ctrl_wr_list, file_ext); | |
357 | spin_unlock_bh(&dev->device_lock); | |
358 | } | |
359 | end: | |
360 | DBG("free connect cb memory."); | |
361 | kfree(req_msg.data); | |
362 | kfree(res_msg.data); | |
363 | kfree(priv_cb); | |
364 | return rets; | |
365 | } | |
366 | ||
367 | /** | |
368 | * heci_ioctl_wd - the wd IOCTL function | |
369 | * | |
370 | * @dev: Device object for our driver | |
371 | * @if_num: minor number | |
372 | * @k_msg: data in kernel on the stack | |
373 | * @file_ext: private data of the file object | |
374 | * | |
375 | * returns 0 on success, <0 on failure. | |
376 | */ | |
377 | int heci_ioctl_wd(struct iamt_heci_device *dev, int if_num, | |
378 | struct heci_message_data k_msg, | |
379 | struct heci_file_private *file_ext) | |
380 | { | |
381 | int rets = 0; | |
382 | struct heci_message_data req_msg; /*in kernel on the stack */ | |
383 | ||
384 | if (if_num != HECI_MINOR_NUMBER) | |
385 | return -ENODEV; | |
386 | ||
387 | spin_lock(&file_ext->file_lock); | |
388 | if (k_msg.size != HECI_WATCHDOG_DATA_SIZE) { | |
389 | DBG("user buffer has invalid size.\n"); | |
390 | spin_unlock(&file_ext->file_lock); | |
391 | return -EMSGSIZE; | |
392 | } | |
393 | spin_unlock(&file_ext->file_lock); | |
394 | ||
395 | req_msg.data = kmalloc(HECI_WATCHDOG_DATA_SIZE, GFP_KERNEL); | |
396 | if (!req_msg.data) { | |
397 | DBG("failed allocation request buffer size = %d.\n", | |
398 | HECI_WATCHDOG_DATA_SIZE); | |
399 | return -ENOMEM; | |
400 | } | |
401 | req_msg.size = HECI_WATCHDOG_DATA_SIZE; | |
402 | ||
403 | /* copy the message to kernel space - use a pointer already | |
404 | * copied into kernel space | |
405 | */ | |
36e84467 DX |
406 | if (copy_from_user(req_msg.data, |
407 | (void __user *)k_msg.data, req_msg.size)) { | |
d52b3d9c MO |
408 | rets = -EFAULT; |
409 | goto end; | |
410 | } | |
411 | spin_lock_bh(&dev->device_lock); | |
412 | if (dev->heci_state != HECI_ENABLED) { | |
413 | rets = -ENODEV; | |
414 | spin_unlock_bh(&dev->device_lock); | |
415 | goto end; | |
416 | } | |
417 | ||
418 | if (dev->wd_file_ext.state != HECI_FILE_CONNECTED) { | |
419 | rets = -ENODEV; | |
420 | spin_unlock_bh(&dev->device_lock); | |
421 | goto end; | |
422 | } | |
423 | if (!dev->asf_mode) { | |
424 | rets = -EIO; | |
425 | spin_unlock_bh(&dev->device_lock); | |
426 | goto end; | |
427 | } | |
428 | ||
429 | memcpy(&dev->wd_data[HECI_WD_PARAMS_SIZE], req_msg.data, | |
430 | HECI_WATCHDOG_DATA_SIZE); | |
431 | ||
432 | dev->wd_timeout = (req_msg.data[1] << 8) + req_msg.data[0]; | |
433 | dev->wd_pending = 0; | |
434 | dev->wd_due_counter = 1; /* next timer */ | |
435 | if (dev->wd_timeout == 0) { | |
436 | memcpy(dev->wd_data, heci_stop_wd_params, | |
437 | HECI_WD_PARAMS_SIZE); | |
438 | } else { | |
439 | memcpy(dev->wd_data, heci_start_wd_params, | |
440 | HECI_WD_PARAMS_SIZE); | |
441 | mod_timer(&dev->wd_timer, jiffies); | |
442 | } | |
443 | spin_unlock_bh(&dev->device_lock); | |
444 | end: | |
445 | kfree(req_msg.data); | |
446 | return rets; | |
447 | } | |
448 | ||
449 | ||
450 | /** | |
451 | * heci_ioctl_bypass_wd - the bypass_wd IOCTL function | |
452 | * | |
453 | * @dev: Device object for our driver | |
454 | * @if_num: minor number | |
455 | * @k_msg: data in kernel on the stack | |
456 | * @file_ext: private data of the file object | |
457 | * | |
458 | * returns 0 on success, <0 on failure. | |
459 | */ | |
460 | int heci_ioctl_bypass_wd(struct iamt_heci_device *dev, int if_num, | |
461 | struct heci_message_data k_msg, | |
462 | struct heci_file_private *file_ext) | |
463 | { | |
464 | __u8 flag = 0; | |
465 | int rets = 0; | |
466 | ||
467 | if (if_num != HECI_MINOR_NUMBER) | |
468 | return -ENODEV; | |
469 | ||
470 | spin_lock(&file_ext->file_lock); | |
471 | if (k_msg.size < 1) { | |
472 | DBG("user buffer less than HECI_WATCHDOG_DATA_SIZE.\n"); | |
473 | spin_unlock(&file_ext->file_lock); | |
474 | return -EMSGSIZE; | |
475 | } | |
476 | spin_unlock(&file_ext->file_lock); | |
36e84467 | 477 | if (copy_from_user(&flag, (void __user *)k_msg.data, 1)) { |
d52b3d9c MO |
478 | rets = -EFAULT; |
479 | goto end; | |
480 | } | |
481 | ||
482 | spin_lock_bh(&dev->device_lock); | |
483 | flag = flag ? (1) : (0); | |
484 | dev->wd_bypass = flag; | |
485 | spin_unlock_bh(&dev->device_lock); | |
486 | end: | |
487 | return rets; | |
488 | } | |
489 | ||
490 | /** | |
491 | * find_pthi_read_list_entry - finds a PTHIlist entry for current file | |
492 | * | |
493 | * @dev: Device object for our driver | |
494 | * @file: pointer to file object | |
495 | * | |
496 | * returns returned a list entry on success, NULL on failure. | |
497 | */ | |
498 | struct heci_cb_private *find_pthi_read_list_entry( | |
499 | struct iamt_heci_device *dev, | |
500 | struct file *file) | |
501 | { | |
502 | struct heci_file_private *file_ext_temp; | |
503 | struct heci_cb_private *priv_cb_pos = NULL; | |
504 | struct heci_cb_private *priv_cb_next = NULL; | |
505 | ||
506 | if ((dev->pthi_read_complete_list.status == 0) && | |
507 | !list_empty(&dev->pthi_read_complete_list.heci_cb.cb_list)) { | |
508 | list_for_each_entry_safe(priv_cb_pos, priv_cb_next, | |
509 | &dev->pthi_read_complete_list.heci_cb.cb_list, cb_list) { | |
510 | file_ext_temp = (struct heci_file_private *) | |
511 | priv_cb_pos->file_private; | |
512 | if ((file_ext_temp != NULL) && | |
513 | (file_ext_temp == &dev->iamthif_file_ext) && | |
514 | (priv_cb_pos->file_object == file)) | |
515 | return priv_cb_pos; | |
516 | } | |
517 | } | |
518 | return NULL; | |
519 | } | |
520 | ||
521 | /** | |
522 | * pthi_read - read data from pthi client | |
523 | * | |
524 | * @dev: Device object for our driver | |
525 | * @if_num: minor number | |
526 | * @file: pointer to file object | |
527 | * @*ubuf: pointer to user data in user space | |
528 | * @length: data length to read | |
529 | * @offset: data read offset | |
530 | * | |
531 | * returns | |
532 | * returned data length on success, | |
533 | * zero if no data to read, | |
534 | * negative on failure. | |
535 | */ | |
536 | int pthi_read(struct iamt_heci_device *dev, int if_num, struct file *file, | |
c4739ea6 | 537 | char __user *ubuf, size_t length, loff_t *offset) |
d52b3d9c MO |
538 | { |
539 | int rets = 0; | |
540 | struct heci_cb_private *priv_cb = NULL; | |
541 | struct heci_file_private *file_ext = file->private_data; | |
542 | __u8 i; | |
543 | unsigned long currtime = get_seconds(); | |
544 | ||
545 | if ((if_num != HECI_MINOR_NUMBER) || (!dev)) | |
546 | return -ENODEV; | |
547 | ||
548 | if ((file_ext == NULL) || (file_ext != &dev->iamthif_file_ext)) | |
549 | return -ENODEV; | |
550 | ||
551 | spin_lock_bh(&dev->device_lock); | |
552 | for (i = 0; i < dev->num_heci_me_clients; i++) { | |
553 | if (dev->me_clients[i].client_id == | |
554 | dev->iamthif_file_ext.me_client_id) | |
555 | break; | |
556 | } | |
557 | BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id); | |
558 | if ((i == dev->num_heci_me_clients) | |
559 | || (dev->me_clients[i].client_id != | |
560 | dev->iamthif_file_ext.me_client_id)) { | |
561 | DBG("PTHI client not found.\n"); | |
562 | spin_unlock_bh(&dev->device_lock); | |
563 | return -ENODEV; | |
564 | } | |
565 | priv_cb = find_pthi_read_list_entry(dev, file); | |
566 | if (!priv_cb) { | |
567 | spin_unlock_bh(&dev->device_lock); | |
568 | return 0; /* No more data to read */ | |
569 | } else { | |
570 | if (priv_cb && | |
571 | (currtime - priv_cb->read_time > IAMTHIF_READ_TIMER)) { | |
572 | /* 15 sec for the message has expired */ | |
573 | list_del(&priv_cb->cb_list); | |
574 | spin_unlock_bh(&dev->device_lock); | |
575 | rets = -ETIMEDOUT; | |
576 | goto free; | |
577 | } | |
578 | /* if the whole message will fit remove it from the list */ | |
579 | if ((priv_cb->information >= *offset) && | |
580 | (length >= (priv_cb->information - *offset))) | |
581 | list_del(&priv_cb->cb_list); | |
582 | else if ((priv_cb->information > 0) && | |
583 | (priv_cb->information <= *offset)) { | |
584 | /* end of the message has been reached */ | |
585 | list_del(&priv_cb->cb_list); | |
586 | rets = 0; | |
587 | spin_unlock_bh(&dev->device_lock); | |
588 | goto free; | |
589 | } | |
590 | /* else means that not full buffer will be read and do not | |
591 | * remove message from deletion list | |
592 | */ | |
593 | } | |
594 | DBG("pthi priv_cb->response_buffer size - %d\n", | |
595 | priv_cb->response_buffer.size); | |
596 | DBG("pthi priv_cb->information - %lu\n", | |
597 | priv_cb->information); | |
598 | spin_unlock_bh(&dev->device_lock); | |
599 | ||
600 | /* length is being turncated to PAGE_SIZE, however, | |
601 | * the information may be longer */ | |
602 | length = length < (priv_cb->information - *offset) ? | |
603 | length : (priv_cb->information - *offset); | |
604 | ||
605 | if (copy_to_user(ubuf, | |
606 | priv_cb->response_buffer.data + *offset, | |
607 | length)) | |
608 | rets = -EFAULT; | |
609 | else { | |
610 | rets = length; | |
611 | if ((*offset + length) < priv_cb->information) { | |
612 | *offset += length; | |
613 | goto out; | |
614 | } | |
615 | } | |
616 | free: | |
617 | DBG("free pthi cb memory.\n"); | |
618 | *offset = 0; | |
619 | heci_free_cb_private(priv_cb); | |
620 | out: | |
621 | return rets; | |
622 | } | |
623 | ||
624 | /** | |
625 | * heci_start_read - the start read client message function. | |
626 | * | |
627 | * @dev: Device object for our driver | |
628 | * @if_num: minor number | |
629 | * @file_ext: private data of the file object | |
630 | * | |
631 | * returns 0 on success, <0 on failure. | |
632 | */ | |
633 | int heci_start_read(struct iamt_heci_device *dev, int if_num, | |
634 | struct heci_file_private *file_ext) | |
635 | { | |
636 | int rets = 0; | |
637 | __u8 i; | |
638 | struct heci_cb_private *priv_cb = NULL; | |
639 | ||
640 | if ((if_num != HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) { | |
641 | DBG("received wrong function input param.\n"); | |
642 | return -ENODEV; | |
643 | } | |
afcf462a DX |
644 | |
645 | spin_lock_bh(&dev->device_lock); | |
72abd228 | 646 | if (file_ext->state != HECI_FILE_CONNECTED) { |
afcf462a | 647 | spin_unlock_bh(&dev->device_lock); |
d52b3d9c | 648 | return -ENODEV; |
72abd228 | 649 | } |
d52b3d9c | 650 | |
d52b3d9c MO |
651 | if (dev->heci_state != HECI_ENABLED) { |
652 | spin_unlock_bh(&dev->device_lock); | |
653 | return -ENODEV; | |
654 | } | |
655 | spin_unlock_bh(&dev->device_lock); | |
656 | DBG("check if read is pending.\n"); | |
58b25a63 | 657 | spin_lock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
658 | if ((file_ext->read_pending) || (file_ext->read_cb != NULL)) { |
659 | DBG("read is pending.\n"); | |
58b25a63 | 660 | spin_unlock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
661 | return -EBUSY; |
662 | } | |
58b25a63 | 663 | spin_unlock_bh(&file_ext->read_io_lock); |
72abd228 | 664 | |
d52b3d9c MO |
665 | priv_cb = kzalloc(sizeof(struct heci_cb_private), GFP_KERNEL); |
666 | if (!priv_cb) | |
667 | return -ENOMEM; | |
668 | ||
58b25a63 | 669 | spin_lock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
670 | DBG("allocation call back success\n" |
671 | "host client = %d, ME client = %d\n", | |
672 | file_ext->host_client_id, file_ext->me_client_id); | |
58b25a63 | 673 | spin_unlock_bh(&file_ext->read_io_lock); |
72abd228 | 674 | |
d52b3d9c | 675 | spin_lock_bh(&dev->device_lock); |
58b25a63 | 676 | spin_lock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
677 | for (i = 0; i < dev->num_heci_me_clients; i++) { |
678 | if (dev->me_clients[i].client_id == file_ext->me_client_id) | |
679 | break; | |
680 | ||
681 | } | |
682 | ||
683 | BUG_ON(dev->me_clients[i].client_id != file_ext->me_client_id); | |
58b25a63 | 684 | spin_unlock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
685 | if (i == dev->num_heci_me_clients) { |
686 | rets = -ENODEV; | |
687 | goto unlock; | |
688 | } | |
689 | ||
690 | priv_cb->response_buffer.size = dev->me_clients[i].props.max_msg_length; | |
691 | spin_unlock_bh(&dev->device_lock); | |
692 | priv_cb->response_buffer.data = | |
693 | kmalloc(priv_cb->response_buffer.size, GFP_KERNEL); | |
694 | if (!priv_cb->response_buffer.data) { | |
695 | rets = -ENOMEM; | |
696 | goto fail; | |
697 | } | |
698 | DBG("allocation call back data success.\n"); | |
699 | priv_cb->major_file_operations = HECI_READ; | |
700 | /* make sure information is zero before we start */ | |
701 | priv_cb->information = 0; | |
702 | priv_cb->file_private = (void *) file_ext; | |
d52b3d9c | 703 | spin_lock_bh(&dev->device_lock); |
58b25a63 | 704 | spin_lock_bh(&file_ext->read_io_lock); |
72abd228 | 705 | file_ext->read_cb = priv_cb; |
d52b3d9c MO |
706 | if (dev->host_buffer_is_empty) { |
707 | dev->host_buffer_is_empty = 0; | |
708 | if (!heci_send_flow_control(dev, file_ext)) { | |
709 | rets = -ENODEV; | |
58b25a63 | 710 | spin_unlock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
711 | goto unlock; |
712 | } else { | |
713 | list_add_tail(&priv_cb->cb_list, | |
714 | &dev->read_list.heci_cb.cb_list); | |
715 | } | |
716 | } else { | |
717 | list_add_tail(&priv_cb->cb_list, | |
718 | &dev->ctrl_wr_list.heci_cb.cb_list); | |
719 | } | |
58b25a63 | 720 | spin_unlock_bh(&file_ext->read_io_lock); |
d52b3d9c MO |
721 | spin_unlock_bh(&dev->device_lock); |
722 | return rets; | |
723 | unlock: | |
724 | spin_unlock_bh(&dev->device_lock); | |
725 | fail: | |
726 | heci_free_cb_private(priv_cb); | |
727 | return rets; | |
728 | } | |
729 | ||
730 | /** | |
731 | * pthi_write - write iamthif data to pthi client | |
732 | * | |
733 | * @dev: Device object for our driver | |
734 | * @priv_cb: heci call back struct | |
735 | * | |
736 | * returns 0 on success, <0 on failure. | |
737 | */ | |
738 | int pthi_write(struct iamt_heci_device *dev, | |
739 | struct heci_cb_private *priv_cb) | |
740 | { | |
741 | int rets = 0; | |
742 | struct heci_msg_hdr heci_hdr; | |
743 | ||
744 | if ((!dev) || (!priv_cb)) | |
745 | return -ENODEV; | |
746 | ||
747 | DBG("write data to pthi client.\n"); | |
748 | ||
749 | dev->iamthif_state = HECI_IAMTHIF_WRITING; | |
750 | dev->iamthif_current_cb = priv_cb; | |
751 | dev->iamthif_file_object = priv_cb->file_object; | |
752 | dev->iamthif_canceled = 0; | |
753 | dev->iamthif_ioctl = 1; | |
754 | dev->iamthif_msg_buf_size = priv_cb->request_buffer.size; | |
755 | memcpy(dev->iamthif_msg_buf, priv_cb->request_buffer.data, | |
756 | priv_cb->request_buffer.size); | |
757 | ||
758 | if (flow_ctrl_creds(dev, &dev->iamthif_file_ext) && | |
759 | dev->host_buffer_is_empty) { | |
760 | dev->host_buffer_is_empty = 0; | |
761 | if (priv_cb->request_buffer.size > | |
762 | (((dev->host_hw_state & H_CBD) >> 24) * | |
763 | sizeof(__u32)) - sizeof(struct heci_msg_hdr)) { | |
764 | heci_hdr.length = | |
765 | (((dev->host_hw_state & H_CBD) >> 24) * | |
766 | sizeof(__u32)) - sizeof(struct heci_msg_hdr); | |
767 | heci_hdr.msg_complete = 0; | |
768 | } else { | |
769 | heci_hdr.length = priv_cb->request_buffer.size; | |
770 | heci_hdr.msg_complete = 1; | |
771 | } | |
772 | ||
773 | heci_hdr.host_addr = dev->iamthif_file_ext.host_client_id; | |
774 | heci_hdr.me_addr = dev->iamthif_file_ext.me_client_id; | |
775 | heci_hdr.reserved = 0; | |
776 | dev->iamthif_msg_buf_index += heci_hdr.length; | |
777 | if (!heci_write_message(dev, &heci_hdr, | |
778 | (unsigned char *)(dev->iamthif_msg_buf), | |
779 | heci_hdr.length)) | |
780 | return -ENODEV; | |
781 | ||
782 | if (heci_hdr.msg_complete) { | |
783 | flow_ctrl_reduce(dev, &dev->iamthif_file_ext); | |
784 | dev->iamthif_flow_control_pending = 1; | |
785 | dev->iamthif_state = HECI_IAMTHIF_FLOW_CONTROL; | |
786 | DBG("add pthi cb to write waiting list\n"); | |
787 | dev->iamthif_current_cb = priv_cb; | |
788 | dev->iamthif_file_object = priv_cb->file_object; | |
789 | list_add_tail(&priv_cb->cb_list, | |
790 | &dev->write_waiting_list.heci_cb.cb_list); | |
791 | } else { | |
792 | DBG("message does not complete, " | |
793 | "so add pthi cb to write list.\n"); | |
794 | list_add_tail(&priv_cb->cb_list, | |
795 | &dev->write_list.heci_cb.cb_list); | |
796 | } | |
797 | } else { | |
798 | if (!(dev->host_buffer_is_empty)) | |
799 | DBG("host buffer is not empty"); | |
800 | ||
801 | DBG("No flow control credentials, " | |
802 | "so add iamthif cb to write list.\n"); | |
803 | list_add_tail(&priv_cb->cb_list, | |
804 | &dev->write_list.heci_cb.cb_list); | |
805 | } | |
806 | return rets; | |
807 | } | |
808 | ||
809 | /** | |
810 | * iamthif_ioctl_send_msg - send cmd data to pthi client | |
811 | * | |
812 | * @dev: Device object for our driver | |
813 | * | |
814 | * returns 0 on success, <0 on failure. | |
815 | */ | |
816 | void run_next_iamthif_cmd(struct iamt_heci_device *dev) | |
817 | { | |
818 | struct heci_file_private *file_ext_tmp; | |
819 | struct heci_cb_private *priv_cb_pos = NULL; | |
820 | struct heci_cb_private *priv_cb_next = NULL; | |
821 | int status = 0; | |
822 | ||
823 | if (!dev) | |
824 | return; | |
825 | ||
826 | dev->iamthif_msg_buf_size = 0; | |
827 | dev->iamthif_msg_buf_index = 0; | |
828 | dev->iamthif_canceled = 0; | |
829 | dev->iamthif_ioctl = 1; | |
830 | dev->iamthif_state = HECI_IAMTHIF_IDLE; | |
831 | dev->iamthif_timer = 0; | |
832 | dev->iamthif_file_object = NULL; | |
833 | ||
834 | if (dev->pthi_cmd_list.status == 0 && | |
835 | !list_empty(&dev->pthi_cmd_list.heci_cb.cb_list)) { | |
836 | DBG("complete pthi cmd_list cb.\n"); | |
837 | ||
838 | list_for_each_entry_safe(priv_cb_pos, priv_cb_next, | |
839 | &dev->pthi_cmd_list.heci_cb.cb_list, cb_list) { | |
840 | list_del(&priv_cb_pos->cb_list); | |
841 | file_ext_tmp = (struct heci_file_private *) | |
842 | priv_cb_pos->file_private; | |
843 | ||
844 | if ((file_ext_tmp != NULL) && | |
845 | (file_ext_tmp == &dev->iamthif_file_ext)) { | |
846 | status = pthi_write(dev, priv_cb_pos); | |
847 | if (status != 0) { | |
848 | DBG("pthi write failed status = %d\n", | |
849 | status); | |
850 | return; | |
851 | } | |
852 | break; | |
853 | } | |
854 | } | |
855 | } | |
856 | } | |
857 | ||
858 | /** | |
859 | * heci_free_cb_private - free heci_cb_private related memory | |
860 | * | |
861 | * @priv_cb: heci callback struct | |
862 | */ | |
863 | void heci_free_cb_private(struct heci_cb_private *priv_cb) | |
864 | { | |
865 | if (priv_cb == NULL) | |
866 | return; | |
867 | ||
868 | kfree(priv_cb->request_buffer.data); | |
869 | kfree(priv_cb->response_buffer.data); | |
870 | kfree(priv_cb); | |
871 | } | |
872 |