xref: /titanic_41/usr/src/uts/intel/io/heci/heci_main.c (revision 7a286c471efbab8562f7655a82931904703fffe0)
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 #pragma ident	"@(#)heci_main.c	1.7	08/03/07 SMI"
47 
48 #include <sys/types.h>
49 #include <sys/note.h>
50 #include <sys/cmn_err.h>
51 #include <sys/conf.h>
52 #include <sys/ddi.h>
53 #include <sys/ddi_impldefs.h>
54 #include <sys/devops.h>
55 #include <sys/instance.h>
56 #include <sys/modctl.h>
57 #include <sys/open.h>
58 #include <sys/stat.h>
59 #include <sys/sunddi.h>
60 #include <sys/file.h>
61 #include <sys/priv.h>
62 #include <sys/systm.h>
63 #include <sys/mkdev.h>
64 #include <sys/list.h>
65 #include <sys/pci.h>
66 #include "heci_data_structures.h"
67 
68 #include "heci.h"
69 #include "heci_interface.h"
70 
71 #define	MAJOR_VERSION	5
72 #define	MINOR_VERSION	0
73 #define	QUICK_FIX_NUMBER	0
74 #define	VER_BUILD	30
75 
76 #define	str(s)	name(s)
77 #define	name(s)	#s
78 #define	HECI_DRIVER_VERSION	str(MAJOR_VERSION) "." str(MINOR_VERSION) \
79 	"." str(QUICK_FIX_NUMBER) "." str(VER_BUILD)
80 
81 #define	HECI_READ_TIMEOUT	45
82 
83 #define	HECI_DRIVER_NAME	"heci"
84 
85 /*
86  *  heci driver strings
87  */
88 char heci_driver_name[] = HECI_DRIVER_NAME;
89 char heci_driver_string[] = "Intel(R) Management Engine Interface";
90 char heci_driver_version[] = HECI_DRIVER_VERSION;
91 char heci_copyright[] = "Copyright (c) 2003 - 2008 Intel Corporation.";
92 
93 void * heci_soft_state_p = NULL;
94 
95 #ifdef DEBUG
96 int heci_debug = 0;
97 #endif
98 
99 /*
100  * Local Function Prototypes
101  */
102 static int heci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
103 static int heci_initialize(dev_info_t *dip, struct iamt_heci_device *device);
104 static int heci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
105 	void *arg, void **result);
106 static int heci_detach(dev_info_t *dip,  ddi_detach_cmd_t cmd);
107 static int heci_quiesce(dev_info_t *dip);
108 static int heci_open(dev_t *devp, int flags, int otyp, cred_t *credp);
109 static int heci_close(dev_t dev, int flag, int otyp, struct cred *cred);
110 static int heci_read(dev_t dev, struct uio *uio_p, cred_t *cred_p);
111 static int heci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
112 	cred_t *cr, int *rval);
113 static int heci_write(dev_t dev, struct uio *uio_p, struct cred *cred);
114 static int heci_poll(dev_t dev, short events, int anyyet,
115 		short *reventsp, struct pollhead **phpp);
116 static struct heci_cb_private *find_read_list_entry(
117 		struct iamt_heci_device *dev,
118 		struct heci_file_private *file_ext);
119 static inline int heci_fe_same_id(struct heci_file_private *fe1,
120 		struct heci_file_private *fe2);
121 
122 static void heci_resume(dev_info_t *dip);
123 static int heci_suspend(dev_info_t *dip);
124 static uint16_t g_sus_wd_timeout;
125 
126 static struct cb_ops heci_cb_ops = {
127 	heci_open,		/* open */
128 	heci_close,		/* close */
129 	nodev,			/* strategy */
130 	nodev,			/* print */
131 	nodev,			/* dump */
132 	heci_read,		/* read */
133 	heci_write,		/* write */
134 	heci_ioctl,		/* ioctl */
135 	nodev,			/* devmap */
136 	nodev,			/* mmap */
137 	nodev,			/* segmap */
138 	heci_poll,		/* poll */
139 	ddi_prop_op,		/* cb_prop op */
140 	NULL,			/* stream tab */
141 	D_MP		/* Driver Compatability Flags */
142 };
143 
144 static struct dev_ops heci_dev_ops = {
145 	DEVO_REV,		/* devo_rev */
146 	0,			/* refcnt */
147 	heci_getinfo,		/* get_dev_info */
148 	nulldev,		/* identify */
149 	nulldev,		/* probe */
150 	heci_attach,		/* attach */
151 	heci_detach,		/* detach */
152 	nodev,			/* reset */
153 	&heci_cb_ops,		/* Driver Ops */
154 	(struct bus_ops *)NULL,	/* Bus Operations */
155 	NULL,			/* power */
156 	heci_quiesce		/* devo_quiesce */
157 };
158 
159 /*
160  * Module linkage information for the kernel
161  */
162 
163 static struct modldrv modldrv = {
164 	&mod_driverops,		/* Type of Module = Driver */
165 	heci_driver_string, 	/* Driver Identifier string. */
166 	&heci_dev_ops,		/* Driver Ops. */
167 };
168 
169 static struct modlinkage modlinkage = {
170 	MODREV_1, (void *)&modldrv, NULL
171 };
172 
173 /*
174  * Module Initialization functions.
175  */
176 
177 int
178 _init(void)
179 {
180 	int stat;
181 
182 	/* Allocate soft state */
183 	if ((stat = ddi_soft_state_init(&heci_soft_state_p,
184 		sizeof (struct iamt_heci_device), 1)) != DDI_SUCCESS) {
185 	    return (stat);
186 	}
187 
188 	if ((stat = mod_install(&modlinkage)) != 0)
189 		ddi_soft_state_fini(&heci_soft_state_p);
190 
191 	return (stat);
192 }
193 
194 int
195 _info(struct modinfo *infop)
196 {
197 
198 	return (mod_info(&modlinkage, infop));
199 }
200 
201 int
202 _fini(void)
203 {
204 	int stat;
205 
206 	if ((stat = mod_remove(&modlinkage)) != 0)
207 		return (stat);
208 
209 	ddi_soft_state_fini(&heci_soft_state_p);
210 
211 	return (stat);
212 }
213 
214 /*
215  * heci_attach - Driver Attach Routine
216  */
217 static int
218 heci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
219 {
220 	int instance, status;
221 	struct iamt_heci_device *device;
222 
223 	switch (cmd) {
224 	case DDI_ATTACH:
225 		break;
226 	case DDI_RESUME:
227 		heci_resume(dip);
228 		return (DDI_SUCCESS);
229 	default:
230 		return (DDI_FAILURE);
231 	}
232 
233 	DBG("%s - version %s\n", heci_driver_string, heci_driver_version);
234 	DBG("%s\n", heci_copyright);
235 
236 	instance = ddi_get_instance(dip);	/* find out which unit */
237 	status = ddi_soft_state_zalloc(heci_soft_state_p, instance);
238 	if (status != DDI_SUCCESS)
239 		return (DDI_FAILURE);
240 	device = ddi_get_soft_state(heci_soft_state_p, instance);
241 	ASSERT(device != NULL);	/* can't fail - we only just allocated it */
242 
243 	device->dip = dip;
244 
245 	status = heci_initialize(dip, device);
246 	if (status != DDI_SUCCESS) {
247 		ddi_soft_state_free(heci_soft_state_p, instance);
248 		return (DDI_FAILURE);
249 	}
250 
251 	status = ddi_create_minor_node(dip, "AMT", S_IFCHR,
252 	    MAKE_MINOR_NUM(HECI_MINOR_NUMBER, instance),
253 	    DDI_PSEUDO, 0);
254 
255 	if (status != DDI_SUCCESS) {
256 
257 		ddi_remove_minor_node(dip, NULL);
258 		ddi_soft_state_free(heci_soft_state_p, instance);
259 		return (DDI_FAILURE);
260 	}
261 
262 
263 	return (status);
264 }
265 
266 /*
267  * heci_probe - Device Initialization Routine
268  */
269 static int
270 heci_initialize(dev_info_t *dip, struct iamt_heci_device *device)
271 {
272 	int err;
273 	ddi_device_acc_attr_t attr;
274 
275 	err = ddi_get_iblock_cookie(dip, 0, &device->sc_iblk);
276 	if (err != DDI_SUCCESS) {
277 		cmn_err(CE_WARN, "heci_probe():"
278 		    " ddi_get_iblock_cookie() failed\n");
279 		goto end;
280 	}
281 	/* initializes the heci device structure */
282 	init_heci_device(dip, device);
283 
284 	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
285 	attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
286 	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
287 
288 	if (ddi_regs_map_setup(dip, 1, (caddr_t *)&device->mem_addr, 0, 0,
289 	    &attr, &device->io_handle) != DDI_SUCCESS) {
290 		cmn_err(CE_WARN, "heci%d: unable to map PCI regs\n",
291 		    ddi_get_instance(dip));
292 		goto fini_heci_device;
293 	}
294 
295 	err = ddi_add_intr(dip, 0, &device->sc_iblk, NULL,
296 	    heci_isr_interrupt, (caddr_t)device);
297 	if (err != DDI_SUCCESS) {
298 		cmn_err(CE_WARN, "heci_probe(): ddi_add_intr() failed\n");
299 		goto unmap_memory;
300 	}
301 
302 	if (heci_hw_init(device)) {
303 		cmn_err(CE_WARN, "init hw failure.\n");
304 		err = -ENODEV;
305 		goto release_irq;
306 	}
307 	(void) heci_initialize_clients(device);
308 	if (device->heci_state != HECI_ENABLED) {
309 		err = -ENODEV;
310 		goto release_hw;
311 	}
312 	if (device->wd_timeout)
313 		device->wd_timer = timeout(heci_wd_timer, device, 1);
314 
315 	DBG("heci driver initialization successful.\n");
316 	return (0);
317 
318 release_hw:
319 	/* disable interrupts */
320 	device->host_hw_state = read_heci_register(device, H_CSR);
321 	heci_csr_disable_interrupts(device);
322 
323 release_irq:
324 	ddi_remove_intr(dip, 0, device->sc_iblk);
325 unmap_memory:
326 	if (device->mem_addr)
327 		ddi_regs_map_free(&device->io_handle);
328 fini_heci_device:
329 	fini_heci_device(device);
330 end:
331 	cmn_err(CE_WARN, "heci driver initialization failed.\n");
332 	return (err);
333 }
334 
335 void
336 heci_destroy_locks(struct iamt_heci_device *device_object)
337 {
338 
339 	mutex_destroy(&device_object->iamthif_file_ext.file_lock);
340 	mutex_destroy(&device_object->iamthif_file_ext.read_io_lock);
341 	mutex_destroy(&device_object->iamthif_file_ext.write_io_lock);
342 
343 	mutex_destroy(&device_object->wd_file_ext.file_lock);
344 	mutex_destroy(&device_object->wd_file_ext.read_io_lock);
345 	mutex_destroy(&device_object->wd_file_ext.write_io_lock);
346 	mutex_destroy(&device_object->device_lock);
347 
348 	cv_destroy(&device_object->iamthif_file_ext.rx_wait);
349 	cv_destroy(&device_object->wd_file_ext.rx_wait);
350 	cv_destroy(&device_object->wait_recvd_msg);
351 	cv_destroy(&device_object->wait_stop_wd);
352 }
353 
354 /*
355  * heci_remove - Device Removal Routine
356  *
357  * @pdev: PCI device information struct
358  *
359  * heci_remove is called by the PCI subsystem to alert the driver
360  * that it should release a PCI device.
361  */
362 static int
363 heci_detach(dev_info_t *dip,  ddi_detach_cmd_t cmd)
364 {
365 	struct iamt_heci_device	*dev;
366 	int err;
367 
368 	dev = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
369 	ASSERT(dev != NULL);
370 
371 	switch (cmd) {
372 	case DDI_SUSPEND:
373 		err = heci_suspend(dip);
374 		if (err)
375 			return (DDI_FAILURE);
376 		else
377 			return (DDI_SUCCESS);
378 
379 	case DDI_DETACH:
380 		break;
381 
382 	default:
383 		return (DDI_FAILURE);
384 	}
385 
386 	if (dev->wd_timer)
387 		(void) untimeout(dev->wd_timer);
388 
389 	mutex_enter(&dev->device_lock);
390 	if (dev->wd_file_ext.state == HECI_FILE_CONNECTED &&
391 	    dev->wd_timeout) {
392 		dev->wd_timeout = 0;
393 		dev->wd_due_counter = 0;
394 		(void) memcpy(dev->wd_data, stop_wd_params,
395 		    HECI_WD_PARAMS_SIZE);
396 		dev->stop = 1;
397 		if (dev->host_buffer_is_empty &&
398 		    flow_ctrl_creds(dev, &dev->wd_file_ext)) {
399 			dev->host_buffer_is_empty = 0;
400 
401 			if (!heci_send_wd(dev)) {
402 				DBG("send stop WD failed\n");
403 			} else
404 				flow_ctrl_reduce(dev, &dev->wd_file_ext);
405 
406 			dev->wd_pending = 0;
407 		} else
408 			dev->wd_pending = 1;
409 
410 		dev->wd_stoped = 0;
411 
412 		err = 0;
413 		while (!dev->wd_stoped && err != -1) {
414 			clock_t tm;
415 			tm = ddi_get_lbolt();
416 			err = cv_timedwait(&dev->wait_stop_wd,
417 			    &dev->device_lock,
418 			    tm + 10*HZ);
419 		}
420 
421 		if (!dev->wd_stoped) {
422 			DBG("stop wd failed to complete.\n");
423 		} else {
424 			DBG("stop wd complete.\n");
425 		}
426 
427 	}
428 
429 	mutex_exit(&dev->device_lock);
430 
431 	if (dev->iamthif_file_ext.state == HECI_FILE_CONNECTED) {
432 		dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTING;
433 		(void) heci_disconnect_host_client(dev,
434 		    &dev->iamthif_file_ext);
435 	}
436 	if (dev->wd_file_ext.state == HECI_FILE_CONNECTED) {
437 		dev->wd_file_ext.state = HECI_FILE_DISCONNECTING;
438 		(void) heci_disconnect_host_client(dev,
439 		    &dev->wd_file_ext);
440 	}
441 
442 
443 	/* remove entry if already in list */
444 	DBG("list del iamthif and wd file list.\n");
445 	heci_remove_client_from_file_list(dev, dev->wd_file_ext.
446 	    host_client_id);
447 	heci_remove_client_from_file_list(dev,
448 	    dev->iamthif_file_ext.host_client_id);
449 
450 	dev->iamthif_current_cb = NULL;
451 	dev->iamthif_file_ext.file = NULL;
452 
453 	/* disable interrupts */
454 	heci_csr_disable_interrupts(dev);
455 
456 	ddi_remove_intr(dip, 0, dev->sc_iblk);
457 
458 	if (dev->work)
459 		ddi_taskq_destroy(dev->work);
460 	if (dev->reinit_tsk)
461 		ddi_taskq_destroy(dev->reinit_tsk);
462 	if (dev->mem_addr)
463 		ddi_regs_map_free(&dev->io_handle);
464 
465 	if (dev->me_clients && dev->num_heci_me_clients > 0) {
466 		kmem_free(dev->me_clients, sizeof (struct heci_me_client) *
467 		    dev->num_heci_me_clients);
468 	}
469 
470 	dev->num_heci_me_clients = 0;
471 
472 	heci_destroy_locks(dev);
473 
474 	ddi_remove_minor_node(dip, NULL);
475 	ddi_soft_state_free(heci_soft_state_p, ddi_get_instance(dip));
476 
477 	return (DDI_SUCCESS);
478 }
479 
480 
481 static int
482 heci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
483 {
484 	int error = DDI_SUCCESS;
485 	struct iamt_heci_device *device;
486 	int minor, instance;
487 
488 	_NOTE(ARGUNUSED(dip))
489 
490 	switch (cmd) {
491 	case DDI_INFO_DEVT2DEVINFO:
492 		minor = getminor((dev_t)arg);
493 		instance = HECI_MINOR_TO_INSTANCE(minor);
494 		if (!(device = ddi_get_soft_state(heci_soft_state_p, instance)))
495 			*result = NULL;
496 		else
497 			*result = device->dip;
498 		break;
499 	case DDI_INFO_DEVT2INSTANCE:
500 		minor = getminor((dev_t)arg);
501 		instance = HECI_MINOR_TO_INSTANCE(minor);
502 		*result = (void *)((long)minor);
503 		break;
504 	default:
505 		error = DDI_FAILURE;
506 		break;
507 	}
508 	return (error);
509 }
510 /*
511  * heci_clear_list - remove all callbacks associated with file
512  * 		from heci_cb_list
513  *
514  * @file: file information struct
515  * @heci_cb_list: callbacks list
516  *
517  * heci_clear_list is called to clear resources associated with file
518  * when application calls close function or Ctrl-C was pressed
519  *
520  * @return 1 if callback removed from the list, 0 otherwise
521  */
522 static int
523 heci_clear_list(struct iamt_heci_device *dev,
524 	struct heci_file *file, struct list_node *heci_cb_list)
525 {
526 	struct heci_cb_private *priv_cb_pos = NULL;
527 	struct heci_cb_private *priv_cb_next = NULL;
528 	struct heci_file *file_temp;
529 	int rets = 0;
530 
531 	/* list all list member */
532 	list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
533 	    heci_cb_list, cb_list, struct heci_cb_private) {
534 		file_temp = (struct heci_file *)priv_cb_pos->file_object;
535 		/* check if list member associated with a file */
536 		if (file_temp == file) {
537 			/* remove member from the list */
538 			list_del(&priv_cb_pos->cb_list);
539 			/* check if cb equal to current iamthif cb */
540 			if (dev->iamthif_current_cb == priv_cb_pos) {
541 				dev->iamthif_current_cb = NULL;
542 				/* send flow control to iamthif client */
543 				if (!heci_send_flow_control(dev,
544 				    &dev->iamthif_file_ext)) {
545 				    DBG("sending flow control failed\n");
546 				}
547 			}
548 			/* free all allocated buffers */
549 			heci_free_cb_private(priv_cb_pos);
550 			rets = 1;
551 		}
552 	}
553 	return (rets);
554 }
555 
556 /*
557  * heci_clear_lists - remove all callbacks associated with file
558  *
559  * @dev: device information struct
560  * @file: file information struct
561  *
562  * heci_clear_lists is called to clear resources associated with file
563  * when application calls close function or Ctrl-C was pressed
564  *
565  * @return 1 if callback removed from the list, 0 otherwise
566  */
567 static int
568 heci_clear_lists(struct iamt_heci_device *dev, struct heci_file *file)
569 {
570 	int rets = 0;
571 
572 	/* remove callbacks associated with a file */
573 	(void) heci_clear_list(dev, file, &dev->pthi_cmd_list.heci_cb.cb_list);
574 	if (heci_clear_list(dev, file,
575 	    &dev->pthi_read_complete_list.heci_cb.cb_list))
576 		rets = 1;
577 
578 	(void) heci_clear_list(dev, file, &dev->ctrl_rd_list.heci_cb.cb_list);
579 
580 	if (heci_clear_list(dev, file, &dev->ctrl_wr_list.heci_cb.cb_list))
581 		rets = 1;
582 
583 	if (heci_clear_list(dev, file,
584 	    &dev->write_waiting_list.heci_cb.cb_list))
585 		rets = 1;
586 
587 	if (heci_clear_list(dev, file, &dev->write_list.heci_cb.cb_list))
588 		rets = 1;
589 
590 	/* check if iamthif_current_cb not NULL */
591 	if (dev->iamthif_current_cb && (!rets)) {
592 		/* check file and iamthif current cb association */
593 		if (dev->iamthif_current_cb->file_object == file) {
594 			/* remove cb */
595 			heci_free_cb_private(dev->iamthif_current_cb);
596 			dev->iamthif_current_cb = NULL;
597 			rets = 1;
598 		}
599 	}
600 	return (rets);
601 }
602 
603 /*
604  * heci_open - the open function
605  */
606 static int
607 heci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
608 {
609 	struct iamt_heci_device *dev;
610 	struct heci_file_private *file_ext = NULL;
611 	int minor, if_num, instance;
612 	struct heci_file *file;
613 
614 	_NOTE(ARGUNUSED(flags, credp))
615 
616 	minor = getminor(*devp);
617 
618 	DBG("heci_open: enter...\n");
619 
620 	/*
621 	 * Make sure the open is for the right file type.
622 	 */
623 	if (otyp != OTYP_CHR)
624 		return (EINVAL);
625 
626 	instance = HECI_MINOR_TO_INSTANCE(minor);
627 	if_num = HECI_MINOR_TO_IFNUM(minor);
628 
629 	dev = ddi_get_soft_state(heci_soft_state_p, instance);
630 
631 	if ((if_num < HECI_MINOR_NUMBER) || (!dev))
632 		return (-ENODEV);
633 
634 	file_ext = heci_alloc_file_private(NULL);
635 	if (file_ext == NULL)
636 		return (-ENOMEM);
637 
638 	mutex_enter(&dev->device_lock);
639 	if (dev->heci_state != HECI_ENABLED) {
640 		mutex_exit(&dev->device_lock);
641 		kmem_free(file_ext, sizeof (struct heci_file_private));
642 		file_ext = NULL;
643 		return (-ENODEV);
644 	}
645 	if (dev->open_handle_count >= HECI_MAX_OPEN_HANDLE_COUNT) {
646 		mutex_exit(&dev->device_lock);
647 		kmem_free(file_ext, sizeof (struct heci_file_private));
648 		file_ext = NULL;
649 		return (-ENFILE);
650 	}
651 	dev->open_handle_count++;
652 	list_add_tail(&file_ext->link, &dev->file_list);
653 	while ((dev->heci_host_clients[dev->current_host_client_id / 8]
654 	    & (1 << (dev->current_host_client_id % 8))) != 0) {
655 
656 		dev->current_host_client_id++;
657 		dev->current_host_client_id %= HECI_MAX_OPEN_HANDLE_COUNT;
658 		DBG("current_host_client_id = %d\n",
659 		    dev->current_host_client_id);
660 		DBG("dev->open_handle_count = %lu\n",
661 		    dev->open_handle_count);
662 	}
663 	DBG("current_host_client_id = %d\n", dev->current_host_client_id);
664 	file_ext->host_client_id = dev->current_host_client_id;
665 	*devp = makedevice(getmajor(*devp),
666 	    MAKE_MINOR_NUM(dev->current_host_client_id, instance));
667 	file = &dev->files[dev->current_host_client_id];
668 	dev->heci_host_clients[file_ext->host_client_id / 8] |=
669 	    (1 << (file_ext->host_client_id % 8));
670 	mutex_exit(&dev->device_lock);
671 	mutex_enter(&file_ext->file_lock);
672 	file_ext->state = HECI_FILE_INITIALIZING;
673 	file_ext->sm_state = 0;
674 
675 	file->private_data = file_ext;
676 	mutex_exit(&file_ext->file_lock);
677 
678 	return (0);
679 }
680 
681 /*
682  * heci_close - the close function
683  */
684 static int
685 heci_close(dev_t devt, int flag, int otyp, struct cred *cred)
686 {
687 	int rets = 0;
688 	int minor, if_num, instance;
689 	struct heci_file_private *file_ext;
690 	struct heci_cb_private *priv_cb = NULL;
691 	struct iamt_heci_device *dev;
692 	struct heci_file *file;
693 
694 	_NOTE(ARGUNUSED(flag, otyp, cred))
695 
696 	minor = getminor(devt);
697 
698 	instance = HECI_MINOR_TO_INSTANCE(minor);
699 	if_num = HECI_MINOR_TO_IFNUM(minor);
700 
701 	dev = ddi_get_soft_state(heci_soft_state_p, instance);
702 
703 	file = &dev->files[if_num];
704 	file_ext = file->private_data;
705 
706 	if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
707 		return (-ENODEV);
708 
709 	if (file_ext != &dev->iamthif_file_ext) {
710 		mutex_enter(&file_ext->file_lock);
711 		if (file_ext->state == HECI_FILE_CONNECTED) {
712 			file_ext->state = HECI_FILE_DISCONNECTING;
713 			mutex_exit(&file_ext->file_lock);
714 			DBG("disconnecting client host client = %d, "
715 			    "ME client = %d\n",
716 			    file_ext->host_client_id,
717 			    file_ext->me_client_id);
718 			rets = heci_disconnect_host_client(dev, file_ext);
719 			mutex_enter(&file_ext->file_lock);
720 		}
721 		mutex_enter(&dev->device_lock);
722 		heci_flush_queues(dev, file_ext);
723 		DBG("remove client host client = %d, ME client = %d\n",
724 		    file_ext->host_client_id,
725 		    file_ext->me_client_id);
726 
727 		if (dev->open_handle_count > 0) {
728 			dev->heci_host_clients[file_ext->host_client_id / 8] &=
729 			    ~(1 << (file_ext->host_client_id % 8));
730 			dev->open_handle_count--;
731 		}
732 		heci_remove_client_from_file_list(dev,
733 		    file_ext->host_client_id);
734 
735 		/* free read cb */
736 		if (file_ext->read_cb != NULL) {
737 			priv_cb = find_read_list_entry(dev, file_ext);
738 			/* Remove entry from read list */
739 			if (priv_cb != NULL)
740 				list_del(&priv_cb->cb_list);
741 
742 			priv_cb = file_ext->read_cb;
743 			file_ext->read_cb = NULL;
744 		}
745 
746 		mutex_exit(&dev->device_lock);
747 		file->private_data = NULL;
748 		mutex_exit(&file_ext->file_lock);
749 
750 		if (priv_cb != NULL)
751 			heci_free_cb_private(priv_cb);
752 
753 		heci_free_file_private(file_ext);
754 	} else {
755 		mutex_enter(&dev->device_lock);
756 
757 		if (dev->open_handle_count > 0)
758 			dev->open_handle_count--;
759 
760 		if (dev->iamthif_file_object == file &&
761 		    dev->iamthif_state != HECI_IAMTHIF_IDLE) {
762 			DBG("pthi canceled iamthif state %d\n",
763 			    dev->iamthif_state);
764 			dev->iamthif_canceled = 1;
765 			if (dev->iamthif_state == HECI_IAMTHIF_READ_COMPLETE) {
766 				DBG("run next pthi iamthif cb\n");
767 				run_next_iamthif_cmd(dev);
768 			}
769 		}
770 
771 		if (heci_clear_lists(dev, file))
772 			dev->iamthif_state = HECI_IAMTHIF_IDLE;
773 
774 		mutex_exit(&dev->device_lock);
775 	}
776 	return (rets);
777 }
778 
779 static struct heci_cb_private *
780 find_read_list_entry(struct iamt_heci_device *dev,
781 	struct heci_file_private *file_ext)
782 {
783 	struct heci_cb_private *priv_cb_pos = NULL;
784 	struct heci_cb_private *priv_cb_next = NULL;
785 	struct heci_file_private *file_ext_list_temp;
786 
787 	if (dev->read_list.status == 0 &&
788 	    !list_empty(&dev->read_list.heci_cb.cb_list)) {
789 
790 		DBG("remove read_list CB \n");
791 		list_for_each_entry_safe(priv_cb_pos,
792 		    priv_cb_next,
793 		    &dev->read_list.heci_cb.cb_list, cb_list,
794 		    struct heci_cb_private) {
795 
796 			file_ext_list_temp = (struct heci_file_private *)
797 			    priv_cb_pos->file_private;
798 
799 			if ((file_ext_list_temp != NULL) &&
800 			    heci_fe_same_id(file_ext, file_ext_list_temp))
801 				return (priv_cb_pos);
802 
803 		}
804 	}
805 	return (NULL);
806 }
807 
808 /*
809  * heci_read - the read client message function.
810  */
811 static int
812 heci_read(dev_t devt, struct uio *uio_p, cred_t *cred_p)
813 {
814 	int i;
815 	int rets = 0;
816 	size_t length;
817 	struct heci_file	*file;
818 	struct heci_file_private *file_ext;
819 	struct heci_cb_private *priv_cb_pos = NULL;
820 	int instance, minor, if_num, err;
821 	struct heci_cb_private *priv_cb = NULL;
822 	struct iamt_heci_device *dev;
823 
824 	_NOTE(ARGUNUSED(cred_p))
825 
826 	minor = getminor(devt);
827 
828 	instance = HECI_MINOR_TO_INSTANCE(minor);
829 	if_num = HECI_MINOR_TO_IFNUM(minor);
830 
831 	dev = ddi_get_soft_state(heci_soft_state_p, instance);
832 
833 	file = &dev->files[if_num];
834 	file_ext = file->private_data;
835 
836 	if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
837 		return (-ENODEV);
838 
839 	mutex_enter(&dev->device_lock);
840 	if (dev->heci_state != HECI_ENABLED) {
841 		mutex_exit(&dev->device_lock);
842 		return (-ENODEV);
843 	}
844 	mutex_exit(&dev->device_lock);
845 	if (!file_ext)
846 		return (-ENODEV);
847 
848 	mutex_enter(&file_ext->file_lock);
849 	if ((file_ext->sm_state & HECI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
850 		mutex_exit(&file_ext->file_lock);
851 		/* Do not allow to read watchdog client */
852 		for (i = 0; i < dev->num_heci_me_clients; i++) {
853 			if (memcmp(&heci_wd_guid,
854 			    &dev->me_clients[i].props.protocol_name,
855 			    sizeof (struct guid)) == 0) {
856 				if (file_ext->me_client_id ==
857 				    dev->me_clients[i].client_id)
858 					return (-EBADF);
859 			}
860 		}
861 	} else {
862 		file_ext->sm_state &= ~HECI_WD_STATE_INDEPENDENCE_MSG_SENT;
863 		mutex_exit(&file_ext->file_lock);
864 	}
865 
866 	if (file_ext == &dev->iamthif_file_ext) {
867 		rets = pthi_read(dev, if_num, file, uio_p);
868 		goto out;
869 	}
870 
871 	if (file_ext->read_cb &&
872 	    file_ext->read_cb->information > UIO_OFFSET(uio_p)) {
873 		priv_cb = file_ext->read_cb;
874 		goto copy_buffer;
875 	} else if (file_ext->read_cb && file_ext->read_cb->information > 0 &&
876 	    file_ext->read_cb->information <= UIO_OFFSET(uio_p)) {
877 		priv_cb = file_ext->read_cb;
878 		rets = 0;
879 		goto free;
880 	} else if (
881 	    (!file_ext->read_cb || file_ext->read_cb->information == 0) &&
882 	    UIO_OFFSET(uio_p) > 0) {
883 		/* Offset needs to be cleaned for contingous reads */
884 		UIO_OFFSET(uio_p) = 0;
885 		rets = 0;
886 		goto out;
887 	}
888 
889 	mutex_enter(&file_ext->read_io_lock);
890 	err = heci_start_read(dev, if_num, file_ext);
891 	if (err != 0 && err != -EBUSY) {
892 		DBG("heci start read failure with status = %d\n", err);
893 		mutex_exit(&file_ext->read_io_lock);
894 		rets = err;
895 		goto out;
896 	}
897 	while (HECI_READ_COMPLETE != file_ext->reading_state &&
898 	    HECI_FILE_INITIALIZING != file_ext->state &&
899 	    HECI_FILE_DISCONNECTED != file_ext->state &&
900 	    HECI_FILE_DISCONNECTING != file_ext->state) {
901 		mutex_exit(&file_ext->read_io_lock);
902 		mutex_enter(&dev->device_lock);
903 		if (cv_wait_sig(&file_ext->rx_wait, &dev->device_lock) == 0) {
904 			mutex_exit(&dev->device_lock);
905 			priv_cb = file_ext->read_cb;
906 			rets = -EINTR;
907 			goto free;
908 		}
909 		mutex_exit(&dev->device_lock);
910 
911 
912 		if (HECI_FILE_INITIALIZING == file_ext->state ||
913 		    HECI_FILE_DISCONNECTED == file_ext->state ||
914 		    HECI_FILE_DISCONNECTING == file_ext->state) {
915 			rets = -EBUSY;
916 			goto out;
917 		}
918 		mutex_enter(&file_ext->read_io_lock);
919 	}
920 
921 	priv_cb = file_ext->read_cb;
922 
923 	if (!priv_cb) {
924 		mutex_exit(&file_ext->read_io_lock);
925 		return (-ENODEV);
926 	}
927 	if (file_ext->reading_state != HECI_READ_COMPLETE) {
928 		mutex_exit(&file_ext->read_io_lock);
929 		return (0);
930 	}
931 	mutex_exit(&file_ext->read_io_lock);
932 	/* now copy the data to user space */
933 copy_buffer:
934 	DBG("priv_cb->response_buffer size - %d\n",
935 	    priv_cb->response_buffer.size);
936 	DBG("priv_cb->information - %lu\n", priv_cb->information);
937 	if (uio_p->uio_resid == 0 || uio_p->uio_resid < priv_cb->information) {
938 		rets = -EMSGSIZE;
939 		goto free;
940 	}
941 	length = (uio_p->uio_resid <
942 	    (priv_cb->information - uio_p->uio_offset) ?
943 	    uio_p->uio_resid : (priv_cb->information - uio_p->uio_offset));
944 
945 	if (uiomove(priv_cb->response_buffer.data,
946 	    length, UIO_READ, uio_p)) {
947 		rets = -EFAULT;
948 		goto free;
949 	}
950 	else
951 		rets = 0;
952 
953 free:
954 	mutex_enter(&dev->device_lock);
955 	priv_cb_pos = find_read_list_entry(dev, file_ext);
956 	/* Remove entry from read list */
957 	if (priv_cb_pos != NULL)
958 		list_del(&priv_cb_pos->cb_list);
959 	mutex_exit(&dev->device_lock);
960 	heci_free_cb_private(priv_cb);
961 	mutex_enter(&file_ext->read_io_lock);
962 	file_ext->reading_state = HECI_IDLE;
963 	file_ext->read_cb = NULL;
964 	file_ext->read_pending = 0;
965 	mutex_exit(&file_ext->read_io_lock);
966 out:	DBG("end heci read rets= %d\n", rets);
967 	return (rets);
968 }
969 
970 /*
971  * heci_write - the write function.
972  */
973 static int
974 heci_write(dev_t devt, struct uio *uio_p, struct cred *cred)
975 {
976 	int rets = 0;
977 	uint8_t i;
978 	size_t length;
979 	struct heci_file_private *file_ext;
980 	struct heci_cb_private *priv_write_cb = NULL;
981 	struct heci_msg_hdr heci_hdr;
982 	struct iamt_heci_device *dev;
983 	unsigned long currtime = ddi_get_time();
984 	int instance, minor, if_num, err;
985 	struct heci_file *file;
986 
987 	_NOTE(ARGUNUSED(cred))
988 	DBG("heci_write enter...\n");
989 
990 	minor = getminor(devt);
991 	instance = HECI_MINOR_TO_INSTANCE(minor);
992 	if_num = HECI_MINOR_TO_IFNUM(minor);
993 
994 	dev = ddi_get_soft_state(heci_soft_state_p, instance);
995 
996 	file = &dev->files[if_num];
997 	file_ext = file->private_data;
998 	if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
999 		return (-ENODEV);
1000 
1001 	mutex_enter(&dev->device_lock);
1002 
1003 	if (dev->heci_state != HECI_ENABLED) {
1004 		mutex_exit(&dev->device_lock);
1005 		return (-ENODEV);
1006 	}
1007 	if (file_ext == &dev->iamthif_file_ext) {
1008 		priv_write_cb = find_pthi_read_list_entry(dev, file);
1009 		if ((priv_write_cb != NULL) &&
1010 		    (((currtime - priv_write_cb->read_time) >
1011 		    IAMTHIF_READ_TIMER) ||
1012 		    (file_ext->reading_state == HECI_READ_COMPLETE))) {
1013 			UIO_OFFSET(uio_p) = 0;
1014 			list_del(&priv_write_cb->cb_list);
1015 			heci_free_cb_private(priv_write_cb);
1016 			priv_write_cb = NULL;
1017 		}
1018 	}
1019 
1020 	/* free entry used in read */
1021 	if (file_ext->reading_state == HECI_READ_COMPLETE) {
1022 		UIO_OFFSET(uio_p) = 0;
1023 		priv_write_cb = find_read_list_entry(dev, file_ext);
1024 		if (priv_write_cb != NULL) {
1025 			list_del(&priv_write_cb->cb_list);
1026 			heci_free_cb_private(priv_write_cb);
1027 			priv_write_cb = NULL;
1028 			mutex_enter(&file_ext->read_io_lock);
1029 			file_ext->reading_state = HECI_IDLE;
1030 			file_ext->read_cb = NULL;
1031 			file_ext->read_pending = 0;
1032 			mutex_exit(&file_ext->read_io_lock);
1033 		}
1034 	} else if (file_ext->reading_state == HECI_IDLE &&
1035 	    file_ext->read_pending == 0)
1036 		UIO_OFFSET(uio_p) = 0;
1037 
1038 	mutex_exit(&dev->device_lock);
1039 
1040 	priv_write_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1041 	if (!priv_write_cb)
1042 		return (-ENOMEM);
1043 
1044 	priv_write_cb->file_object = file;
1045 	priv_write_cb->file_private = file_ext;
1046 	priv_write_cb->request_buffer.data =
1047 	    kmem_zalloc(uio_p->uio_resid, KM_SLEEP);
1048 	if (!priv_write_cb->request_buffer.data) {
1049 		kmem_free(priv_write_cb, sizeof (struct heci_cb_private));
1050 		return (-ENOMEM);
1051 	}
1052 	length = (int)uio_p->uio_resid;
1053 	DBG("length =%d\n", (int)length);
1054 
1055 	err = uiomove(priv_write_cb->request_buffer.data,
1056 	    length, UIO_WRITE, uio_p);
1057 	if (err) {
1058 		rets = err;
1059 		goto fail;
1060 	}
1061 
1062 #define	UBUFF	UIO_BUFF(uio_p)
1063 
1064 	mutex_enter(&file_ext->file_lock);
1065 	file_ext->sm_state = 0;
1066 	if ((length == 4) &&
1067 	    ((memcmp(heci_wd_state_independence_msg[0], UBUFF, 4) == 0) ||
1068 	    (memcmp(heci_wd_state_independence_msg[1], UBUFF, 4) == 0) ||
1069 	    (memcmp(heci_wd_state_independence_msg[2], UBUFF, 4) == 0)))
1070 
1071 		file_ext->sm_state |= HECI_WD_STATE_INDEPENDENCE_MSG_SENT;
1072 
1073 	mutex_exit(&file_ext->file_lock);
1074 
1075 	LIST_INIT_HEAD(&priv_write_cb->cb_list);
1076 	if (file_ext == &dev->iamthif_file_ext) {
1077 		priv_write_cb->response_buffer.data =
1078 		    kmem_zalloc(IAMTHIF_MTU, KM_SLEEP);
1079 		if (!priv_write_cb->response_buffer.data) {
1080 			rets = -ENOMEM;
1081 			goto fail;
1082 		}
1083 		mutex_enter(&dev->device_lock);
1084 		if (dev->heci_state != HECI_ENABLED) {
1085 			mutex_exit(&dev->device_lock);
1086 			rets = -ENODEV;
1087 			goto fail;
1088 		}
1089 		for (i = 0; i < dev->num_heci_me_clients; i++) {
1090 			if (dev->me_clients[i].client_id ==
1091 			    dev->iamthif_file_ext.me_client_id)
1092 				break;
1093 		}
1094 
1095 		ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
1096 		if ((i == dev->num_heci_me_clients) ||
1097 		    (dev->me_clients[i].client_id !=
1098 		    dev->iamthif_file_ext.me_client_id)) {
1099 
1100 			mutex_exit(&dev->device_lock);
1101 			rets = -ENODEV;
1102 			goto fail;
1103 		} else if ((length >
1104 		    dev->me_clients[i].props.max_msg_length) ||
1105 		    (length == 0)) {
1106 			mutex_exit(&dev->device_lock);
1107 			rets = -EMSGSIZE;
1108 			goto fail;
1109 		}
1110 
1111 
1112 		priv_write_cb->response_buffer.size = IAMTHIF_MTU;
1113 		priv_write_cb->major_file_operations = HECI_IOCTL;
1114 		priv_write_cb->information = 0;
1115 		priv_write_cb->request_buffer.size = (uint32_t)length;
1116 		if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) {
1117 			mutex_exit(&dev->device_lock);
1118 			rets = -ENODEV;
1119 			goto fail;
1120 		}
1121 
1122 		if (!list_empty(&dev->pthi_cmd_list.heci_cb.cb_list) ||
1123 		    dev->iamthif_state != HECI_IAMTHIF_IDLE) {
1124 			DBG("pthi_state = %d\n", (int)dev->iamthif_state);
1125 			DBG("add PTHI cb to pthi cmd waiting list\n");
1126 			list_add_tail(&priv_write_cb->cb_list,
1127 			    &dev->pthi_cmd_list.heci_cb.cb_list);
1128 			rets = 0; /* length; */
1129 		} else {
1130 			DBG("call pthi write\n");
1131 			rets = pthi_write(dev, priv_write_cb);
1132 
1133 			if (rets != 0) {
1134 				DBG("pthi write failed with status = %d\n",
1135 				    rets);
1136 				mutex_exit(&dev->device_lock);
1137 				goto fail;
1138 			}
1139 			rets = 0; /* length; */
1140 		}
1141 		mutex_exit(&dev->device_lock);
1142 		return (rets);
1143 	}
1144 
1145 	priv_write_cb->major_file_operations = HECI_WRITE;
1146 	/* make sure information is zero before we start */
1147 
1148 	priv_write_cb->information = 0;
1149 	priv_write_cb->request_buffer.size = (uint32_t)length;
1150 
1151 	mutex_enter(&dev->device_lock);
1152 	mutex_enter(&file_ext->write_io_lock);
1153 	DBG("host client = %d, ME client = %d\n",
1154 	    file_ext->host_client_id, file_ext->me_client_id);
1155 	if (file_ext->state != HECI_FILE_CONNECTED) {
1156 		rets = -ENODEV;
1157 		DBG("host client = %d,  is not connected to ME client = %d",
1158 		    file_ext->host_client_id,
1159 		    file_ext->me_client_id);
1160 
1161 		goto unlock;
1162 	}
1163 	for (i = 0; i < dev->num_heci_me_clients; i++) {
1164 		if (dev->me_clients[i].client_id ==
1165 		    file_ext->me_client_id)
1166 			break;
1167 	}
1168 	ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
1169 	if (i == dev->num_heci_me_clients) {
1170 		rets = -ENODEV;
1171 		goto unlock;
1172 	}
1173 	if (length > dev->me_clients[i].props.max_msg_length || length == 0) {
1174 		rets = -EINVAL;
1175 		goto unlock;
1176 	}
1177 	priv_write_cb->file_private = file_ext;
1178 
1179 	if (flow_ctrl_creds(dev, file_ext) &&
1180 	    dev->host_buffer_is_empty) {
1181 		dev->host_buffer_is_empty = 0;
1182 		if (length > ((((dev->host_hw_state & H_CBD) >> 24) *
1183 			sizeof (uint32_t)) - sizeof (struct heci_msg_hdr))) {
1184 
1185 			heci_hdr.length =
1186 			    (((dev->host_hw_state & H_CBD) >> 24) *
1187 			    sizeof (uint32_t)) -
1188 			    sizeof (struct heci_msg_hdr);
1189 			heci_hdr.msg_complete = 0;
1190 		} else {
1191 			heci_hdr.length = (uint32_t)length;
1192 			heci_hdr.msg_complete = 1;
1193 		}
1194 		heci_hdr.host_addr = file_ext->host_client_id;
1195 		heci_hdr.me_addr = file_ext->me_client_id;
1196 		heci_hdr.reserved = 0;
1197 		DBG("call heci_write_message header=%08x.\n",
1198 		    *((uint32_t *)(void *)&heci_hdr));
1199 		/*  protect heci low level write */
1200 		if (!heci_write_message(dev, &heci_hdr,
1201 		    (unsigned char *)(priv_write_cb->request_buffer.data),
1202 		    heci_hdr.length)) {
1203 
1204 		mutex_exit(&file_ext->write_io_lock);
1205 			mutex_exit(&dev->device_lock);
1206 			heci_free_cb_private(priv_write_cb);
1207 			rets = -ENODEV;
1208 			priv_write_cb->information = 0;
1209 			return (rets);
1210 		}
1211 		file_ext->writing_state = HECI_WRITING;
1212 		priv_write_cb->information = heci_hdr.length;
1213 		if (heci_hdr.msg_complete) {
1214 			flow_ctrl_reduce(dev, file_ext);
1215 			list_add_tail(&priv_write_cb->cb_list,
1216 			    &dev->write_waiting_list.heci_cb.cb_list);
1217 		} else {
1218 			list_add_tail(&priv_write_cb->cb_list,
1219 			    &dev->write_list.heci_cb.cb_list);
1220 		}
1221 
1222 	} else {
1223 
1224 		priv_write_cb->information = 0;
1225 		file_ext->writing_state = HECI_WRITING;
1226 		list_add_tail(&priv_write_cb->cb_list,
1227 		    &dev->write_list.heci_cb.cb_list);
1228 	}
1229 	mutex_exit(&file_ext->write_io_lock);
1230 	mutex_exit(&dev->device_lock);
1231 	return (0);
1232 
1233 unlock:
1234 	mutex_exit(&file_ext->write_io_lock);
1235 	mutex_exit(&dev->device_lock);
1236 fail:
1237 	heci_free_cb_private(priv_write_cb);
1238 	return (rets);
1239 
1240 }
1241 
1242 /*
1243  * heci_ioctl - the IOCTL function
1244  */
1245 static int
1246 heci_ioctl(dev_t devt, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
1247 {
1248 	int rets = 0;
1249 	struct heci_file_private *file_ext;
1250 	/* in user space */
1251 	struct heci_message_data *u_msg = (struct heci_message_data *)arg;
1252 	struct heci_message_data k_msg;	/* all in kernel on the stack */
1253 	struct iamt_heci_device *dev;
1254 	int instance, minor, if_num;
1255 	struct heci_file *file;
1256 
1257 	_NOTE(ARGUNUSED(cr, rval))
1258 
1259 	minor = getminor(devt);
1260 
1261 	instance = HECI_MINOR_TO_INSTANCE(minor);
1262 	if_num = HECI_MINOR_TO_IFNUM(minor);
1263 
1264 	dev = ddi_get_soft_state(heci_soft_state_p, instance);
1265 
1266 	file = &dev->files[if_num];
1267 	file_ext = file->private_data;
1268 
1269 	if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
1270 		return (-ENODEV);
1271 
1272 	mutex_enter(&dev->device_lock);
1273 	if (dev->heci_state != HECI_ENABLED) {
1274 		mutex_exit(&dev->device_lock);
1275 		return (-ENODEV);
1276 	}
1277 	mutex_exit(&dev->device_lock);
1278 
1279 	/* first copy from user all data needed */
1280 	if (ddi_copyin(u_msg, &k_msg, sizeof (k_msg), mode)) {
1281 		DBG("first copy from user all data needed filled\n");
1282 		return (-EFAULT);
1283 	}
1284 #ifdef _LP64
1285 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1286 		uint32_t    addr32 = (uint32_t)(uint64_t)k_msg.data;
1287 		k_msg.data = (char *)(uint64_t)addr32;
1288 		DBG("IPL32: k_msg.data=%p\n", (void *)k_msg.data);
1289 	}
1290 #endif
1291 	DBG("user message size is %d, cmd = 0x%x\n", k_msg.size, cmd);
1292 
1293 	switch (cmd) {
1294 	case IOCTL_HECI_GET_VERSION:
1295 		DBG(": IOCTL_HECI_GET_VERSION\n");
1296 		rets = heci_ioctl_get_version(dev, if_num, u_msg, k_msg,
1297 		    file_ext, mode);
1298 		break;
1299 
1300 	case IOCTL_HECI_CONNECT_CLIENT:
1301 		DBG(": IOCTL_HECI_CONNECT_CLIENT.\n");
1302 		rets = heci_ioctl_connect_client(dev, if_num, u_msg, k_msg,
1303 		    file, mode);
1304 		break;
1305 
1306 	case IOCTL_HECI_WD:
1307 		DBG(": IOCTL_HECI_WD.\n");
1308 		rets = heci_ioctl_wd(dev, if_num, k_msg, file_ext, mode);
1309 		break;
1310 
1311 	case IOCTL_HECI_BYPASS_WD:
1312 		DBG(": IOCTL_HECI_BYPASS_WD.\n");
1313 		rets = heci_ioctl_bypass_wd(dev, if_num, k_msg, file_ext, mode);
1314 		break;
1315 
1316 	default:
1317 		rets = -EINVAL;
1318 		break;
1319 	}
1320 	return (rets);
1321 }
1322 
1323 /*
1324  * heci_poll - the poll function
1325  */
1326 static int
1327 heci_poll(dev_t devt, short events, int anyyet,
1328 		short *reventsp, struct pollhead **phpp)
1329 {
1330 	struct heci_file *file;
1331 	struct heci_file_private *file_extension;
1332 	struct iamt_heci_device *device = NULL;
1333 	int instance, minor, if_num;
1334 
1335 	_NOTE(ARGUNUSED(events))
1336 
1337 	minor = getminor(devt);
1338 
1339 	instance = HECI_MINOR_TO_INSTANCE(minor);
1340 	if_num = HECI_MINOR_TO_IFNUM(minor);
1341 
1342 	device = ddi_get_soft_state(heci_soft_state_p, instance);
1343 
1344 	file = &device->files[if_num];
1345 	file_extension = file->private_data;
1346 
1347 	if ((if_num < HECI_MINOR_NUMBER) || (!device) || (!file_extension))
1348 		return (-ENODEV);
1349 
1350 	mutex_enter(&device->device_lock);
1351 	if (device->heci_state != HECI_ENABLED) {
1352 		mutex_exit(&device->device_lock);
1353 		return (-ENXIO);
1354 
1355 	}
1356 
1357 	mutex_exit(&device->device_lock);
1358 
1359 	if (file_extension == &device->iamthif_file_ext) {
1360 
1361 		mutex_enter(&device->iamthif_file_ext.file_lock);
1362 
1363 		if (device->iamthif_state == HECI_IAMTHIF_READ_COMPLETE &&
1364 		    device->iamthif_file_object == file) {
1365 			*reventsp |= (POLLIN | POLLRDNORM);
1366 			mutex_enter(&device->device_lock);
1367 			DBG("heci_poll: run next pthi cb\n");
1368 			run_next_iamthif_cmd(device);
1369 			mutex_exit(&device->device_lock);
1370 		} else {
1371 			DBG("heci_poll: iamthif no event\n");
1372 			*reventsp = 0;
1373 			if (!anyyet)
1374 				*phpp = &device->iamthif_file_ext.pollwait;
1375 		}
1376 		mutex_exit(&device->iamthif_file_ext.file_lock);
1377 
1378 	} else {
1379 		mutex_enter(&file_extension->write_io_lock);
1380 		if (HECI_WRITE_COMPLETE == file_extension->writing_state) {
1381 			*reventsp |= (POLLIN | POLLRDNORM);
1382 			DBG("heci_poll: file_extension poll event\n");
1383 		} else {
1384 			DBG("heci_poll: file_extension no event\n");
1385 			*reventsp = 0;
1386 			if (!anyyet)
1387 				*phpp = &file_extension->tx_pollwait;
1388 		}
1389 		mutex_exit(&file_extension->write_io_lock);
1390 
1391 
1392 	}
1393 
1394 	return (0);
1395 }
1396 
1397 /*
1398  * heci_fe_same_id - tell if file private data have same id
1399  *
1400  * @fe1: private data of 1. file object
1401  * @fe2: private data of 2. file object
1402  *
1403  * @return  !=0 - if ids are the same, 0 - if differ.
1404  */
1405 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1406 		struct heci_file_private *fe2)
1407 {
1408 	return ((fe1->host_client_id == fe2->host_client_id) &&
1409 	    (fe1->me_client_id == fe2->me_client_id));
1410 }
1411 
1412 /*
1413  * Since the ME firmware won't reset itself during OS reboot, it's not enough
1414  * to only disable interrupts in quiesce(), here we do a full hand-shake
1415  * with the firmware.
1416  */
1417 static int
1418 heci_quiesce(dev_info_t *dip)
1419 {
1420 	struct iamt_heci_device	*dev;
1421 	int err;
1422 
1423 	dev = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1424 	ASSERT(dev != NULL);
1425 
1426 	if (dev->wd_timer)
1427 		(void) untimeout(dev->wd_timer);
1428 
1429 	mutex_enter(&dev->device_lock);
1430 	if (dev->wd_file_ext.state == HECI_FILE_CONNECTED &&
1431 	    dev->wd_timeout) {
1432 		dev->wd_timeout = 0;
1433 		dev->wd_due_counter = 0;
1434 		(void) memcpy(dev->wd_data, stop_wd_params,
1435 		    HECI_WD_PARAMS_SIZE);
1436 		dev->stop = 1;
1437 		if (dev->host_buffer_is_empty &&
1438 		    flow_ctrl_creds(dev, &dev->wd_file_ext)) {
1439 			dev->host_buffer_is_empty = 0;
1440 
1441 			if (!heci_send_wd(dev)) {
1442 				DBG("send stop WD failed\n");
1443 			} else
1444 				flow_ctrl_reduce(dev, &dev->wd_file_ext);
1445 
1446 			dev->wd_pending = 0;
1447 		} else
1448 			dev->wd_pending = 1;
1449 		dev->wd_stoped = 0;
1450 
1451 		err = 0;
1452 		while (!dev->wd_stoped && err != -1) {
1453 			clock_t tm;
1454 			tm = ddi_get_lbolt();
1455 			err = cv_timedwait(&dev->wait_stop_wd,
1456 			    &dev->device_lock,
1457 			    tm + 10*HZ);
1458 		}
1459 
1460 		if (!dev->wd_stoped) {
1461 			DBG("stop wd failed to complete.\n");
1462 		} else {
1463 			DBG("stop wd complete.\n");
1464 		}
1465 
1466 	}
1467 
1468 	mutex_exit(&dev->device_lock);
1469 
1470 	if (dev->iamthif_file_ext.state == HECI_FILE_CONNECTED) {
1471 		dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTING;
1472 		(void) heci_disconnect_host_client(dev,
1473 		    &dev->iamthif_file_ext);
1474 	}
1475 	if (dev->wd_file_ext.state == HECI_FILE_CONNECTED) {
1476 		dev->wd_file_ext.state = HECI_FILE_DISCONNECTING;
1477 		(void) heci_disconnect_host_client(dev,
1478 		    &dev->wd_file_ext);
1479 	}
1480 
1481 
1482 	/* disable interrupts */
1483 	heci_csr_disable_interrupts(dev);
1484 
1485 	return (DDI_SUCCESS);
1486 }
1487 
1488 static int
1489 heci_suspend(dev_info_t *dip)
1490 {
1491 	struct iamt_heci_device *device;
1492 	int err = 0;
1493 
1494 	device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1495 
1496 	if (device->reinit_tsk)
1497 		ddi_taskq_wait(device->reinit_tsk);
1498 
1499 	/* Stop watchdog if exists */
1500 	if (device->wd_timer)
1501 		(void) untimeout(device->wd_timer);
1502 
1503 	mutex_enter(&device->device_lock);
1504 
1505 	if (device->wd_file_ext.state == HECI_FILE_CONNECTED &&
1506 	    device->wd_timeout) {
1507 		g_sus_wd_timeout = device->wd_timeout;
1508 		device->wd_timeout = 0;
1509 		device->wd_due_counter = 0;
1510 		(void) memcpy(device->wd_data, stop_wd_params,
1511 		    HECI_WD_PARAMS_SIZE);
1512 		device->stop = 1;
1513 		if (device->host_buffer_is_empty &&
1514 		    flow_ctrl_creds(device, &device->wd_file_ext)) {
1515 			device->host_buffer_is_empty = 0;
1516 			if (!heci_send_wd(device)) {
1517 				DBG("send stop WD failed\n");
1518 			}
1519 			else
1520 				flow_ctrl_reduce(device, &device->wd_file_ext);
1521 
1522 			device->wd_pending = 0;
1523 		} else {
1524 			device->wd_pending = 1;
1525 		}
1526 		device->wd_stoped = 0;
1527 
1528 		err = 0;
1529 		while (!device->wd_stoped && err != -1) {
1530 			clock_t tm;
1531 			tm = ddi_get_lbolt();
1532 			err = cv_timedwait(&device->wait_stop_wd,
1533 			    &device->device_lock,
1534 			    tm + 10*HZ);
1535 		}
1536 
1537 		if (!device->wd_stoped) {
1538 			DBG("stop wd failed to complete.\n");
1539 		} else {
1540 			DBG("stop wd complete %d.\n", err);
1541 			err = 0;
1542 		}
1543 	}
1544 	/* Set new heci state */
1545 	if (device->heci_state == HECI_ENABLED ||
1546 	    device->heci_state == HECI_RECOVERING_FROM_RESET) {
1547 		device->heci_state = HECI_POWER_DOWN;
1548 		heci_reset(device, 0);
1549 	}
1550 
1551 	/* Here interrupts are already disabled by heci_reset() */
1552 
1553 	mutex_exit(&device->device_lock);
1554 
1555 
1556 	return (err);
1557 }
1558 
1559 static void
1560 heci_resume(dev_info_t *dip)
1561 {
1562 	struct iamt_heci_device *device;
1563 
1564 	device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1565 
1566 	mutex_enter(&device->device_lock);
1567 	device->heci_state = HECI_POWER_UP;
1568 	heci_reset(device, 1);
1569 	mutex_exit(&device->device_lock);
1570 
1571 	/* Start watchdog if stopped in suspend */
1572 	if (g_sus_wd_timeout != 0) {
1573 		device->wd_timeout = g_sus_wd_timeout;
1574 
1575 		(void) memcpy(device->wd_data, start_wd_params,
1576 		    HECI_WD_PARAMS_SIZE);
1577 		(void) memcpy(device->wd_data + HECI_WD_PARAMS_SIZE,
1578 		    &device->wd_timeout, sizeof (uint16_t));
1579 		device->wd_due_counter = 1;
1580 
1581 		if (device->wd_timeout)
1582 			device->wd_timer = timeout(heci_wd_timer, device, 1);
1583 
1584 		g_sus_wd_timeout = 0;
1585 	}
1586 }
1587