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