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                         err = cv_reltimedwait(&dev->wait_stop_wd,
 415                             &dev->device_lock, 10*HZ, TR_CLOCK_TICK);
 416                 }
 417 
 418                 if (!dev->wd_stoped) {
 419                         DBG("stop wd failed to complete.\n");
 420                 } else {
 421                         DBG("stop wd complete.\n");
 422                 }
 423 
 424         }
 425 
 426         mutex_exit(&dev->device_lock);
 427 
 428         if (dev->iamthif_file_ext.state == HECI_FILE_CONNECTED) {
 429                 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTING;
 430                 (void) heci_disconnect_host_client(dev,
 431                     &dev->iamthif_file_ext);
 432         }
 433         if (dev->wd_file_ext.state == HECI_FILE_CONNECTED) {
 434                 dev->wd_file_ext.state = HECI_FILE_DISCONNECTING;
 435                 (void) heci_disconnect_host_client(dev,
 436                     &dev->wd_file_ext);
 437         }
 438 
 439 
 440         /* remove entry if already in list */
 441         DBG("list del iamthif and wd file list.\n");
 442         heci_remove_client_from_file_list(dev, dev->wd_file_ext.
 443             host_client_id);
 444         heci_remove_client_from_file_list(dev,
 445             dev->iamthif_file_ext.host_client_id);
 446 
 447         dev->iamthif_current_cb = NULL;
 448         dev->iamthif_file_ext.file = NULL;
 449 
 450         /* disable interrupts */
 451         heci_csr_disable_interrupts(dev);
 452 
 453         ddi_remove_intr(dip, 0, dev->sc_iblk);
 454 
 455         if (dev->work)
 456                 ddi_taskq_destroy(dev->work);
 457         if (dev->reinit_tsk)
 458                 ddi_taskq_destroy(dev->reinit_tsk);
 459         if (dev->mem_addr)
 460                 ddi_regs_map_free(&dev->io_handle);
 461 
 462         if (dev->me_clients && dev->num_heci_me_clients > 0) {
 463                 kmem_free(dev->me_clients, sizeof (struct heci_me_client) *
 464                     dev->num_heci_me_clients);
 465         }
 466 
 467         dev->num_heci_me_clients = 0;
 468 
 469         heci_destroy_locks(dev);
 470 
 471         ddi_remove_minor_node(dip, NULL);
 472         ddi_soft_state_free(heci_soft_state_p, ddi_get_instance(dip));
 473 
 474         return (DDI_SUCCESS);
 475 }
 476 
 477 
 478 static int
 479 heci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
 480 {
 481         int error = DDI_SUCCESS;
 482         struct iamt_heci_device *device;
 483         int minor, instance;
 484 
 485         _NOTE(ARGUNUSED(dip))
 486 
 487         switch (cmd) {
 488         case DDI_INFO_DEVT2DEVINFO:
 489                 minor = getminor((dev_t)arg);
 490                 instance = HECI_MINOR_TO_INSTANCE(minor);
 491                 if (!(device = ddi_get_soft_state(heci_soft_state_p, instance)))
 492                         *result = NULL;
 493                 else
 494                         *result = device->dip;
 495                 break;
 496         case DDI_INFO_DEVT2INSTANCE:
 497                 minor = getminor((dev_t)arg);
 498                 instance = HECI_MINOR_TO_INSTANCE(minor);
 499                 *result = (void *)((long)minor);
 500                 break;
 501         default:
 502                 error = DDI_FAILURE;
 503                 break;
 504         }
 505         return (error);
 506 }
 507 /*
 508  * heci_clear_list - remove all callbacks associated with file
 509  *              from heci_cb_list
 510  *
 511  * @file: file information struct
 512  * @heci_cb_list: callbacks list
 513  *
 514  * heci_clear_list is called to clear resources associated with file
 515  * when application calls close function or Ctrl-C was pressed
 516  *
 517  * @return 1 if callback removed from the list, 0 otherwise
 518  */
 519 static int
 520 heci_clear_list(struct iamt_heci_device *dev,
 521         struct heci_file *file, struct list_node *heci_cb_list)
 522 {
 523         struct heci_cb_private *priv_cb_pos = NULL;
 524         struct heci_cb_private *priv_cb_next = NULL;
 525         struct heci_file *file_temp;
 526         int rets = 0;
 527 
 528         /* list all list member */
 529         list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
 530             heci_cb_list, cb_list, struct heci_cb_private) {
 531                 file_temp = (struct heci_file *)priv_cb_pos->file_object;
 532                 /* check if list member associated with a file */
 533                 if (file_temp == file) {
 534                         /* remove member from the list */
 535                         list_del(&priv_cb_pos->cb_list);
 536                         /* check if cb equal to current iamthif cb */
 537                         if (dev->iamthif_current_cb == priv_cb_pos) {
 538                                 dev->iamthif_current_cb = NULL;
 539                                 /* send flow control to iamthif client */
 540                                 if (!heci_send_flow_control(dev,
 541                                     &dev->iamthif_file_ext)) {
 542                                     DBG("sending flow control failed\n");
 543                                 }
 544                         }
 545                         /* free all allocated buffers */
 546                         heci_free_cb_private(priv_cb_pos);
 547                         rets = 1;
 548                 }
 549         }
 550         return (rets);
 551 }
 552 
 553 /*
 554  * heci_clear_lists - remove all callbacks associated with file
 555  *
 556  * @dev: device information struct
 557  * @file: file information struct
 558  *
 559  * heci_clear_lists is called to clear resources associated with file
 560  * when application calls close function or Ctrl-C was pressed
 561  *
 562  * @return 1 if callback removed from the list, 0 otherwise
 563  */
 564 static int
 565 heci_clear_lists(struct iamt_heci_device *dev, struct heci_file *file)
 566 {
 567         int rets = 0;
 568 
 569         /* remove callbacks associated with a file */
 570         (void) heci_clear_list(dev, file, &dev->pthi_cmd_list.heci_cb.cb_list);
 571         if (heci_clear_list(dev, file,
 572             &dev->pthi_read_complete_list.heci_cb.cb_list))
 573                 rets = 1;
 574 
 575         (void) heci_clear_list(dev, file, &dev->ctrl_rd_list.heci_cb.cb_list);
 576 
 577         if (heci_clear_list(dev, file, &dev->ctrl_wr_list.heci_cb.cb_list))
 578                 rets = 1;
 579 
 580         if (heci_clear_list(dev, file,
 581             &dev->write_waiting_list.heci_cb.cb_list))
 582                 rets = 1;
 583 
 584         if (heci_clear_list(dev, file, &dev->write_list.heci_cb.cb_list))
 585                 rets = 1;
 586 
 587         /* check if iamthif_current_cb not NULL */
 588         if (dev->iamthif_current_cb && (!rets)) {
 589                 /* check file and iamthif current cb association */
 590                 if (dev->iamthif_current_cb->file_object == file) {
 591                         /* remove cb */
 592                         heci_free_cb_private(dev->iamthif_current_cb);
 593                         dev->iamthif_current_cb = NULL;
 594                         rets = 1;
 595                 }
 596         }
 597         return (rets);
 598 }
 599 
 600 /*
 601  * heci_open - the open function
 602  */
 603 static int
 604 heci_open(dev_t *devp, int flags, int otyp, cred_t *credp)
 605 {
 606         struct iamt_heci_device *dev;
 607         struct heci_file_private *file_ext = NULL;
 608         int minor, if_num, instance;
 609         struct heci_file *file;
 610 
 611         _NOTE(ARGUNUSED(flags, credp))
 612 
 613         minor = getminor(*devp);
 614 
 615         DBG("heci_open: enter...\n");
 616 
 617         /*
 618          * Make sure the open is for the right file type.
 619          */
 620         if (otyp != OTYP_CHR)
 621                 return (EINVAL);
 622 
 623         instance = HECI_MINOR_TO_INSTANCE(minor);
 624         if_num = HECI_MINOR_TO_IFNUM(minor);
 625 
 626         dev = ddi_get_soft_state(heci_soft_state_p, instance);
 627 
 628         if ((if_num < HECI_MINOR_NUMBER) || (!dev))
 629                 return (-ENODEV);
 630 
 631         file_ext = heci_alloc_file_private(NULL);
 632         if (file_ext == NULL)
 633                 return (-ENOMEM);
 634 
 635         mutex_enter(&dev->device_lock);
 636         if (dev->heci_state != HECI_ENABLED) {
 637                 mutex_exit(&dev->device_lock);
 638                 kmem_free(file_ext, sizeof (struct heci_file_private));
 639                 file_ext = NULL;
 640                 return (-ENODEV);
 641         }
 642         if (dev->open_handle_count >= HECI_MAX_OPEN_HANDLE_COUNT) {
 643                 mutex_exit(&dev->device_lock);
 644                 kmem_free(file_ext, sizeof (struct heci_file_private));
 645                 file_ext = NULL;
 646                 return (-ENFILE);
 647         }
 648         dev->open_handle_count++;
 649         list_add_tail(&file_ext->link, &dev->file_list);
 650         while ((dev->heci_host_clients[dev->current_host_client_id / 8]
 651             & (1 << (dev->current_host_client_id % 8))) != 0) {
 652 
 653                 dev->current_host_client_id++;
 654                 dev->current_host_client_id %= HECI_MAX_OPEN_HANDLE_COUNT;
 655                 DBG("current_host_client_id = %d\n",
 656                     dev->current_host_client_id);
 657                 DBG("dev->open_handle_count = %lu\n",
 658                     dev->open_handle_count);
 659         }
 660         DBG("current_host_client_id = %d\n", dev->current_host_client_id);
 661         file_ext->host_client_id = dev->current_host_client_id;
 662         *devp = makedevice(getmajor(*devp),
 663             MAKE_MINOR_NUM(dev->current_host_client_id, instance));
 664         file = &dev->files[dev->current_host_client_id];
 665         dev->heci_host_clients[file_ext->host_client_id / 8] |=
 666             (1 << (file_ext->host_client_id % 8));
 667         mutex_exit(&dev->device_lock);
 668         mutex_enter(&file_ext->file_lock);
 669         file_ext->state = HECI_FILE_INITIALIZING;
 670         file_ext->sm_state = 0;
 671 
 672         file->private_data = file_ext;
 673         mutex_exit(&file_ext->file_lock);
 674 
 675         return (0);
 676 }
 677 
 678 /*
 679  * heci_close - the close function
 680  */
 681 static int
 682 heci_close(dev_t devt, int flag, int otyp, struct cred *cred)
 683 {
 684         int rets = 0;
 685         int minor, if_num, instance;
 686         struct heci_file_private *file_ext;
 687         struct heci_cb_private *priv_cb = NULL;
 688         struct iamt_heci_device *dev;
 689         struct heci_file *file;
 690 
 691         _NOTE(ARGUNUSED(flag, otyp, cred))
 692 
 693         minor = getminor(devt);
 694 
 695         instance = HECI_MINOR_TO_INSTANCE(minor);
 696         if_num = HECI_MINOR_TO_IFNUM(minor);
 697 
 698         dev = ddi_get_soft_state(heci_soft_state_p, instance);
 699 
 700         file = &dev->files[if_num];
 701         file_ext = file->private_data;
 702 
 703         if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
 704                 return (-ENODEV);
 705 
 706         if (file_ext != &dev->iamthif_file_ext) {
 707                 mutex_enter(&file_ext->file_lock);
 708                 if (file_ext->state == HECI_FILE_CONNECTED) {
 709                         file_ext->state = HECI_FILE_DISCONNECTING;
 710                         mutex_exit(&file_ext->file_lock);
 711                         DBG("disconnecting client host client = %d, "
 712                             "ME client = %d\n",
 713                             file_ext->host_client_id,
 714                             file_ext->me_client_id);
 715                         rets = heci_disconnect_host_client(dev, file_ext);
 716                         mutex_enter(&file_ext->file_lock);
 717                 }
 718                 mutex_enter(&dev->device_lock);
 719                 heci_flush_queues(dev, file_ext);
 720                 DBG("remove client host client = %d, ME client = %d\n",
 721                     file_ext->host_client_id,
 722                     file_ext->me_client_id);
 723 
 724                 if (dev->open_handle_count > 0) {
 725                         dev->heci_host_clients[file_ext->host_client_id / 8] &=
 726                             ~(1 << (file_ext->host_client_id % 8));
 727                         dev->open_handle_count--;
 728                 }
 729                 heci_remove_client_from_file_list(dev,
 730                     file_ext->host_client_id);
 731 
 732                 /* free read cb */
 733                 if (file_ext->read_cb != NULL) {
 734                         priv_cb = find_read_list_entry(dev, file_ext);
 735                         /* Remove entry from read list */
 736                         if (priv_cb != NULL)
 737                                 list_del(&priv_cb->cb_list);
 738 
 739                         priv_cb = file_ext->read_cb;
 740                         file_ext->read_cb = NULL;
 741                 }
 742 
 743                 mutex_exit(&dev->device_lock);
 744                 file->private_data = NULL;
 745                 mutex_exit(&file_ext->file_lock);
 746 
 747                 if (priv_cb != NULL)
 748                         heci_free_cb_private(priv_cb);
 749 
 750                 heci_free_file_private(file_ext);
 751         } else {
 752                 mutex_enter(&dev->device_lock);
 753 
 754                 if (dev->open_handle_count > 0)
 755                         dev->open_handle_count--;
 756 
 757                 if (dev->iamthif_file_object == file &&
 758                     dev->iamthif_state != HECI_IAMTHIF_IDLE) {
 759                         DBG("pthi canceled iamthif state %d\n",
 760                             dev->iamthif_state);
 761                         dev->iamthif_canceled = 1;
 762                         if (dev->iamthif_state == HECI_IAMTHIF_READ_COMPLETE) {
 763                                 DBG("run next pthi iamthif cb\n");
 764                                 run_next_iamthif_cmd(dev);
 765                         }
 766                 }
 767 
 768                 if (heci_clear_lists(dev, file))
 769                         dev->iamthif_state = HECI_IAMTHIF_IDLE;
 770 
 771                 mutex_exit(&dev->device_lock);
 772         }
 773         return (rets);
 774 }
 775 
 776 static struct heci_cb_private *
 777 find_read_list_entry(struct iamt_heci_device *dev,
 778         struct heci_file_private *file_ext)
 779 {
 780         struct heci_cb_private *priv_cb_pos = NULL;
 781         struct heci_cb_private *priv_cb_next = NULL;
 782         struct heci_file_private *file_ext_list_temp;
 783 
 784         if (dev->read_list.status == 0 &&
 785             !list_empty(&dev->read_list.heci_cb.cb_list)) {
 786 
 787                 DBG("remove read_list CB \n");
 788                 list_for_each_entry_safe(priv_cb_pos,
 789                     priv_cb_next,
 790                     &dev->read_list.heci_cb.cb_list, cb_list,
 791                     struct heci_cb_private) {
 792 
 793                         file_ext_list_temp = (struct heci_file_private *)
 794                             priv_cb_pos->file_private;
 795 
 796                         if ((file_ext_list_temp != NULL) &&
 797                             heci_fe_same_id(file_ext, file_ext_list_temp))
 798                                 return (priv_cb_pos);
 799 
 800                 }
 801         }
 802         return (NULL);
 803 }
 804 
 805 /*
 806  * heci_read - the read client message function.
 807  */
 808 static int
 809 heci_read(dev_t devt, struct uio *uio_p, cred_t *cred_p)
 810 {
 811         int i;
 812         int rets = 0;
 813         size_t length;
 814         struct heci_file        *file;
 815         struct heci_file_private *file_ext;
 816         struct heci_cb_private *priv_cb_pos = NULL;
 817         int instance, minor, if_num, err;
 818         struct heci_cb_private *priv_cb = NULL;
 819         struct iamt_heci_device *dev;
 820 
 821         _NOTE(ARGUNUSED(cred_p))
 822 
 823         minor = getminor(devt);
 824 
 825         instance = HECI_MINOR_TO_INSTANCE(minor);
 826         if_num = HECI_MINOR_TO_IFNUM(minor);
 827 
 828         dev = ddi_get_soft_state(heci_soft_state_p, instance);
 829 
 830         file = &dev->files[if_num];
 831         file_ext = file->private_data;
 832 
 833         if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
 834                 return (-ENODEV);
 835 
 836         mutex_enter(&dev->device_lock);
 837         if (dev->heci_state != HECI_ENABLED) {
 838                 mutex_exit(&dev->device_lock);
 839                 return (-ENODEV);
 840         }
 841         mutex_exit(&dev->device_lock);
 842         if (!file_ext)
 843                 return (-ENODEV);
 844 
 845         mutex_enter(&file_ext->file_lock);
 846         if ((file_ext->sm_state & HECI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
 847                 mutex_exit(&file_ext->file_lock);
 848                 /* Do not allow to read watchdog client */
 849                 for (i = 0; i < dev->num_heci_me_clients; i++) {
 850                         if (memcmp(&heci_wd_guid,
 851                             &dev->me_clients[i].props.protocol_name,
 852                             sizeof (struct guid)) == 0) {
 853                                 if (file_ext->me_client_id ==
 854                                     dev->me_clients[i].client_id)
 855                                         return (-EBADF);
 856                         }
 857                 }
 858         } else {
 859                 file_ext->sm_state &= ~HECI_WD_STATE_INDEPENDENCE_MSG_SENT;
 860                 mutex_exit(&file_ext->file_lock);
 861         }
 862 
 863         if (file_ext == &dev->iamthif_file_ext) {
 864                 rets = pthi_read(dev, if_num, file, uio_p);
 865                 goto out;
 866         }
 867 
 868         if (file_ext->read_cb &&
 869             file_ext->read_cb->information > UIO_OFFSET(uio_p)) {
 870                 priv_cb = file_ext->read_cb;
 871                 goto copy_buffer;
 872         } else if (file_ext->read_cb && file_ext->read_cb->information > 0 &&
 873             file_ext->read_cb->information <= UIO_OFFSET(uio_p)) {
 874                 priv_cb = file_ext->read_cb;
 875                 rets = 0;
 876                 goto free;
 877         } else if (
 878             (!file_ext->read_cb || file_ext->read_cb->information == 0) &&
 879             UIO_OFFSET(uio_p) > 0) {
 880                 /* Offset needs to be cleaned for contingous reads */
 881                 UIO_OFFSET(uio_p) = 0;
 882                 rets = 0;
 883                 goto out;
 884         }
 885 
 886         mutex_enter(&file_ext->read_io_lock);
 887         err = heci_start_read(dev, if_num, file_ext);
 888         if (err != 0 && err != -EBUSY) {
 889                 DBG("heci start read failure with status = %d\n", err);
 890                 mutex_exit(&file_ext->read_io_lock);
 891                 rets = err;
 892                 goto out;
 893         }
 894         while (HECI_READ_COMPLETE != file_ext->reading_state &&
 895             HECI_FILE_INITIALIZING != file_ext->state &&
 896             HECI_FILE_DISCONNECTED != file_ext->state &&
 897             HECI_FILE_DISCONNECTING != file_ext->state) {
 898                 mutex_exit(&file_ext->read_io_lock);
 899                 mutex_enter(&dev->device_lock);
 900                 if (cv_wait_sig(&file_ext->rx_wait, &dev->device_lock) == 0) {
 901                         mutex_exit(&dev->device_lock);
 902                         priv_cb = file_ext->read_cb;
 903                         rets = -EINTR;
 904                         goto free;
 905                 }
 906                 mutex_exit(&dev->device_lock);
 907 
 908 
 909                 if (HECI_FILE_INITIALIZING == file_ext->state ||
 910                     HECI_FILE_DISCONNECTED == file_ext->state ||
 911                     HECI_FILE_DISCONNECTING == file_ext->state) {
 912                         rets = -EBUSY;
 913                         goto out;
 914                 }
 915                 mutex_enter(&file_ext->read_io_lock);
 916         }
 917 
 918         priv_cb = file_ext->read_cb;
 919 
 920         if (!priv_cb) {
 921                 mutex_exit(&file_ext->read_io_lock);
 922                 return (-ENODEV);
 923         }
 924         if (file_ext->reading_state != HECI_READ_COMPLETE) {
 925                 mutex_exit(&file_ext->read_io_lock);
 926                 return (0);
 927         }
 928         mutex_exit(&file_ext->read_io_lock);
 929         /* now copy the data to user space */
 930 copy_buffer:
 931         DBG("priv_cb->response_buffer size - %d\n",
 932             priv_cb->response_buffer.size);
 933         DBG("priv_cb->information - %lu\n", priv_cb->information);
 934         if (uio_p->uio_resid == 0 || uio_p->uio_resid < priv_cb->information) {
 935                 rets = -EMSGSIZE;
 936                 goto free;
 937         }
 938         length = (uio_p->uio_resid <
 939             (priv_cb->information - uio_p->uio_offset) ?
 940             uio_p->uio_resid : (priv_cb->information - uio_p->uio_offset));
 941 
 942         if (uiomove(priv_cb->response_buffer.data,
 943             length, UIO_READ, uio_p)) {
 944                 rets = -EFAULT;
 945                 goto free;
 946         }
 947         else
 948                 rets = 0;
 949 
 950 free:
 951         mutex_enter(&dev->device_lock);
 952         priv_cb_pos = find_read_list_entry(dev, file_ext);
 953         /* Remove entry from read list */
 954         if (priv_cb_pos != NULL)
 955                 list_del(&priv_cb_pos->cb_list);
 956         mutex_exit(&dev->device_lock);
 957         heci_free_cb_private(priv_cb);
 958         mutex_enter(&file_ext->read_io_lock);
 959         file_ext->reading_state = HECI_IDLE;
 960         file_ext->read_cb = NULL;
 961         file_ext->read_pending = 0;
 962         mutex_exit(&file_ext->read_io_lock);
 963 out:    DBG("end heci read rets= %d\n", rets);
 964         return (rets);
 965 }
 966 
 967 /*
 968  * heci_write - the write function.
 969  */
 970 static int
 971 heci_write(dev_t devt, struct uio *uio_p, struct cred *cred)
 972 {
 973         int rets = 0;
 974         uint8_t i;
 975         size_t length;
 976         struct heci_file_private *file_ext;
 977         struct heci_cb_private *priv_write_cb = NULL;
 978         struct heci_msg_hdr heci_hdr;
 979         struct iamt_heci_device *dev;
 980         unsigned long currtime = ddi_get_time();
 981         int instance, minor, if_num, err;
 982         struct heci_file *file;
 983 
 984         _NOTE(ARGUNUSED(cred))
 985         DBG("heci_write enter...\n");
 986 
 987         minor = getminor(devt);
 988         instance = HECI_MINOR_TO_INSTANCE(minor);
 989         if_num = HECI_MINOR_TO_IFNUM(minor);
 990 
 991         dev = ddi_get_soft_state(heci_soft_state_p, instance);
 992 
 993         file = &dev->files[if_num];
 994         file_ext = file->private_data;
 995         if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
 996                 return (-ENODEV);
 997 
 998         mutex_enter(&dev->device_lock);
 999 
1000         if (dev->heci_state != HECI_ENABLED) {
1001                 mutex_exit(&dev->device_lock);
1002                 return (-ENODEV);
1003         }
1004         if (file_ext == &dev->iamthif_file_ext) {
1005                 priv_write_cb = find_pthi_read_list_entry(dev, file);
1006                 if ((priv_write_cb != NULL) &&
1007                     (((currtime - priv_write_cb->read_time) >
1008                     IAMTHIF_READ_TIMER) ||
1009                     (file_ext->reading_state == HECI_READ_COMPLETE))) {
1010                         UIO_OFFSET(uio_p) = 0;
1011                         list_del(&priv_write_cb->cb_list);
1012                         heci_free_cb_private(priv_write_cb);
1013                         priv_write_cb = NULL;
1014                 }
1015         }
1016 
1017         /* free entry used in read */
1018         if (file_ext->reading_state == HECI_READ_COMPLETE) {
1019                 UIO_OFFSET(uio_p) = 0;
1020                 priv_write_cb = find_read_list_entry(dev, file_ext);
1021                 if (priv_write_cb != NULL) {
1022                         list_del(&priv_write_cb->cb_list);
1023                         heci_free_cb_private(priv_write_cb);
1024                         priv_write_cb = NULL;
1025                         mutex_enter(&file_ext->read_io_lock);
1026                         file_ext->reading_state = HECI_IDLE;
1027                         file_ext->read_cb = NULL;
1028                         file_ext->read_pending = 0;
1029                         mutex_exit(&file_ext->read_io_lock);
1030                 }
1031         } else if (file_ext->reading_state == HECI_IDLE &&
1032             file_ext->read_pending == 0)
1033                 UIO_OFFSET(uio_p) = 0;
1034 
1035         mutex_exit(&dev->device_lock);
1036 
1037         priv_write_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1038         if (!priv_write_cb)
1039                 return (-ENOMEM);
1040 
1041         priv_write_cb->file_object = file;
1042         priv_write_cb->file_private = file_ext;
1043         priv_write_cb->request_buffer.data =
1044             kmem_zalloc(uio_p->uio_resid, KM_SLEEP);
1045         if (!priv_write_cb->request_buffer.data) {
1046                 kmem_free(priv_write_cb, sizeof (struct heci_cb_private));
1047                 return (-ENOMEM);
1048         }
1049         length = (int)uio_p->uio_resid;
1050         DBG("length =%d\n", (int)length);
1051 
1052         err = uiomove(priv_write_cb->request_buffer.data,
1053             length, UIO_WRITE, uio_p);
1054         if (err) {
1055                 rets = err;
1056                 goto fail;
1057         }
1058 
1059 #define UBUFF   UIO_BUFF(uio_p)
1060 
1061         mutex_enter(&file_ext->file_lock);
1062         file_ext->sm_state = 0;
1063         if ((length == 4) &&
1064             ((memcmp(heci_wd_state_independence_msg[0], UBUFF, 4) == 0) ||
1065             (memcmp(heci_wd_state_independence_msg[1], UBUFF, 4) == 0) ||
1066             (memcmp(heci_wd_state_independence_msg[2], UBUFF, 4) == 0)))
1067 
1068                 file_ext->sm_state |= HECI_WD_STATE_INDEPENDENCE_MSG_SENT;
1069 
1070         mutex_exit(&file_ext->file_lock);
1071 
1072         LIST_INIT_HEAD(&priv_write_cb->cb_list);
1073         if (file_ext == &dev->iamthif_file_ext) {
1074                 priv_write_cb->response_buffer.data =
1075                     kmem_zalloc(IAMTHIF_MTU, KM_SLEEP);
1076                 if (!priv_write_cb->response_buffer.data) {
1077                         rets = -ENOMEM;
1078                         goto fail;
1079                 }
1080                 mutex_enter(&dev->device_lock);
1081                 if (dev->heci_state != HECI_ENABLED) {
1082                         mutex_exit(&dev->device_lock);
1083                         rets = -ENODEV;
1084                         goto fail;
1085                 }
1086                 for (i = 0; i < dev->num_heci_me_clients; i++) {
1087                         if (dev->me_clients[i].client_id ==
1088                             dev->iamthif_file_ext.me_client_id)
1089                                 break;
1090                 }
1091 
1092                 ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
1093                 if ((i == dev->num_heci_me_clients) ||
1094                     (dev->me_clients[i].client_id !=
1095                     dev->iamthif_file_ext.me_client_id)) {
1096 
1097                         mutex_exit(&dev->device_lock);
1098                         rets = -ENODEV;
1099                         goto fail;
1100                 } else if ((length >
1101                     dev->me_clients[i].props.max_msg_length) ||
1102                     (length == 0)) {
1103                         mutex_exit(&dev->device_lock);
1104                         rets = -EMSGSIZE;
1105                         goto fail;
1106                 }
1107 
1108 
1109                 priv_write_cb->response_buffer.size = IAMTHIF_MTU;
1110                 priv_write_cb->major_file_operations = HECI_IOCTL;
1111                 priv_write_cb->information = 0;
1112                 priv_write_cb->request_buffer.size = (uint32_t)length;
1113                 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) {
1114                         mutex_exit(&dev->device_lock);
1115                         rets = -ENODEV;
1116                         goto fail;
1117                 }
1118 
1119                 if (!list_empty(&dev->pthi_cmd_list.heci_cb.cb_list) ||
1120                     dev->iamthif_state != HECI_IAMTHIF_IDLE) {
1121                         DBG("pthi_state = %d\n", (int)dev->iamthif_state);
1122                         DBG("add PTHI cb to pthi cmd waiting list\n");
1123                         list_add_tail(&priv_write_cb->cb_list,
1124                             &dev->pthi_cmd_list.heci_cb.cb_list);
1125                         rets = 0; /* length; */
1126                 } else {
1127                         DBG("call pthi write\n");
1128                         rets = pthi_write(dev, priv_write_cb);
1129 
1130                         if (rets != 0) {
1131                                 DBG("pthi write failed with status = %d\n",
1132                                     rets);
1133                                 mutex_exit(&dev->device_lock);
1134                                 goto fail;
1135                         }
1136                         rets = 0; /* length; */
1137                 }
1138                 mutex_exit(&dev->device_lock);
1139                 return (rets);
1140         }
1141 
1142         priv_write_cb->major_file_operations = HECI_WRITE;
1143         /* make sure information is zero before we start */
1144 
1145         priv_write_cb->information = 0;
1146         priv_write_cb->request_buffer.size = (uint32_t)length;
1147 
1148         mutex_enter(&dev->device_lock);
1149         mutex_enter(&file_ext->write_io_lock);
1150         DBG("host client = %d, ME client = %d\n",
1151             file_ext->host_client_id, file_ext->me_client_id);
1152         if (file_ext->state != HECI_FILE_CONNECTED) {
1153                 rets = -ENODEV;
1154                 DBG("host client = %d,  is not connected to ME client = %d",
1155                     file_ext->host_client_id,
1156                     file_ext->me_client_id);
1157 
1158                 goto unlock;
1159         }
1160         for (i = 0; i < dev->num_heci_me_clients; i++) {
1161                 if (dev->me_clients[i].client_id ==
1162                     file_ext->me_client_id)
1163                         break;
1164         }
1165         ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id);
1166         if (i == dev->num_heci_me_clients) {
1167                 rets = -ENODEV;
1168                 goto unlock;
1169         }
1170         if (length > dev->me_clients[i].props.max_msg_length || length == 0) {
1171                 rets = -EINVAL;
1172                 goto unlock;
1173         }
1174         priv_write_cb->file_private = file_ext;
1175 
1176         if (flow_ctrl_creds(dev, file_ext) &&
1177             dev->host_buffer_is_empty) {
1178                 dev->host_buffer_is_empty = 0;
1179                 if (length > ((((dev->host_hw_state & H_CBD) >> 24) *
1180                         sizeof (uint32_t)) - sizeof (struct heci_msg_hdr))) {
1181 
1182                         heci_hdr.length =
1183                             (((dev->host_hw_state & H_CBD) >> 24) *
1184                             sizeof (uint32_t)) -
1185                             sizeof (struct heci_msg_hdr);
1186                         heci_hdr.msg_complete = 0;
1187                 } else {
1188                         heci_hdr.length = (uint32_t)length;
1189                         heci_hdr.msg_complete = 1;
1190                 }
1191                 heci_hdr.host_addr = file_ext->host_client_id;
1192                 heci_hdr.me_addr = file_ext->me_client_id;
1193                 heci_hdr.reserved = 0;
1194                 DBG("call heci_write_message header=%08x.\n",
1195                     *((uint32_t *)(void *)&heci_hdr));
1196                 /*  protect heci low level write */
1197                 if (!heci_write_message(dev, &heci_hdr,
1198                     (unsigned char *)(priv_write_cb->request_buffer.data),
1199                     heci_hdr.length)) {
1200 
1201                 mutex_exit(&file_ext->write_io_lock);
1202                         mutex_exit(&dev->device_lock);
1203                         heci_free_cb_private(priv_write_cb);
1204                         rets = -ENODEV;
1205                         priv_write_cb->information = 0;
1206                         return (rets);
1207                 }
1208                 file_ext->writing_state = HECI_WRITING;
1209                 priv_write_cb->information = heci_hdr.length;
1210                 if (heci_hdr.msg_complete) {
1211                         flow_ctrl_reduce(dev, file_ext);
1212                         list_add_tail(&priv_write_cb->cb_list,
1213                             &dev->write_waiting_list.heci_cb.cb_list);
1214                 } else {
1215                         list_add_tail(&priv_write_cb->cb_list,
1216                             &dev->write_list.heci_cb.cb_list);
1217                 }
1218 
1219         } else {
1220 
1221                 priv_write_cb->information = 0;
1222                 file_ext->writing_state = HECI_WRITING;
1223                 list_add_tail(&priv_write_cb->cb_list,
1224                     &dev->write_list.heci_cb.cb_list);
1225         }
1226         mutex_exit(&file_ext->write_io_lock);
1227         mutex_exit(&dev->device_lock);
1228         return (0);
1229 
1230 unlock:
1231         mutex_exit(&file_ext->write_io_lock);
1232         mutex_exit(&dev->device_lock);
1233 fail:
1234         heci_free_cb_private(priv_write_cb);
1235         return (rets);
1236 
1237 }
1238 
1239 /*
1240  * heci_ioctl - the IOCTL function
1241  */
1242 static int
1243 heci_ioctl(dev_t devt, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
1244 {
1245         int rets = 0;
1246         struct heci_file_private *file_ext;
1247         /* in user space */
1248         struct heci_message_data *u_msg = (struct heci_message_data *)arg;
1249         struct heci_message_data k_msg; /* all in kernel on the stack */
1250         struct iamt_heci_device *dev;
1251         int instance, minor, if_num;
1252         struct heci_file *file;
1253 
1254         _NOTE(ARGUNUSED(cr, rval))
1255 
1256         minor = getminor(devt);
1257 
1258         instance = HECI_MINOR_TO_INSTANCE(minor);
1259         if_num = HECI_MINOR_TO_IFNUM(minor);
1260 
1261         dev = ddi_get_soft_state(heci_soft_state_p, instance);
1262 
1263         file = &dev->files[if_num];
1264         file_ext = file->private_data;
1265 
1266         if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext))
1267                 return (-ENODEV);
1268 
1269         mutex_enter(&dev->device_lock);
1270         if (dev->heci_state != HECI_ENABLED) {
1271                 mutex_exit(&dev->device_lock);
1272                 return (-ENODEV);
1273         }
1274         mutex_exit(&dev->device_lock);
1275 
1276         /* first copy from user all data needed */
1277         if (ddi_copyin(u_msg, &k_msg, sizeof (k_msg), mode)) {
1278                 DBG("first copy from user all data needed filled\n");
1279                 return (-EFAULT);
1280         }
1281 #ifdef _LP64
1282         if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
1283                 uint32_t    addr32 = (uint32_t)(uint64_t)k_msg.data;
1284                 k_msg.data = (char *)(uint64_t)addr32;
1285                 DBG("IPL32: k_msg.data=%p\n", (void *)k_msg.data);
1286         }
1287 #endif
1288         DBG("user message size is %d, cmd = 0x%x\n", k_msg.size, cmd);
1289 
1290         switch (cmd) {
1291         case IOCTL_HECI_GET_VERSION:
1292                 DBG(": IOCTL_HECI_GET_VERSION\n");
1293                 rets = heci_ioctl_get_version(dev, if_num, u_msg, k_msg,
1294                     file_ext, mode);
1295                 break;
1296 
1297         case IOCTL_HECI_CONNECT_CLIENT:
1298                 DBG(": IOCTL_HECI_CONNECT_CLIENT.\n");
1299                 rets = heci_ioctl_connect_client(dev, if_num, u_msg, k_msg,
1300                     file, mode);
1301                 break;
1302 
1303         case IOCTL_HECI_WD:
1304                 DBG(": IOCTL_HECI_WD.\n");
1305                 rets = heci_ioctl_wd(dev, if_num, k_msg, file_ext, mode);
1306                 break;
1307 
1308         case IOCTL_HECI_BYPASS_WD:
1309                 DBG(": IOCTL_HECI_BYPASS_WD.\n");
1310                 rets = heci_ioctl_bypass_wd(dev, if_num, k_msg, file_ext, mode);
1311                 break;
1312 
1313         default:
1314                 rets = -EINVAL;
1315                 break;
1316         }
1317         return (rets);
1318 }
1319 
1320 /*
1321  * heci_poll - the poll function
1322  */
1323 static int
1324 heci_poll(dev_t devt, short events, int anyyet,
1325                 short *reventsp, struct pollhead **phpp)
1326 {
1327         struct heci_file *file;
1328         struct heci_file_private *file_extension;
1329         struct iamt_heci_device *device = NULL;
1330         int instance, minor, if_num;
1331 
1332         _NOTE(ARGUNUSED(events))
1333 
1334         minor = getminor(devt);
1335 
1336         instance = HECI_MINOR_TO_INSTANCE(minor);
1337         if_num = HECI_MINOR_TO_IFNUM(minor);
1338 
1339         device = ddi_get_soft_state(heci_soft_state_p, instance);
1340 
1341         file = &device->files[if_num];
1342         file_extension = file->private_data;
1343 
1344         if ((if_num < HECI_MINOR_NUMBER) || (!device) || (!file_extension))
1345                 return (-ENODEV);
1346 
1347         mutex_enter(&device->device_lock);
1348         if (device->heci_state != HECI_ENABLED) {
1349                 mutex_exit(&device->device_lock);
1350                 return (-ENXIO);
1351 
1352         }
1353 
1354         mutex_exit(&device->device_lock);
1355 
1356         if (file_extension == &device->iamthif_file_ext) {
1357 
1358                 mutex_enter(&device->iamthif_file_ext.file_lock);
1359 
1360                 if (device->iamthif_state == HECI_IAMTHIF_READ_COMPLETE &&
1361                     device->iamthif_file_object == file) {
1362                         *reventsp |= (POLLIN | POLLRDNORM);
1363                         mutex_enter(&device->device_lock);
1364                         DBG("heci_poll: run next pthi cb\n");
1365                         run_next_iamthif_cmd(device);
1366                         mutex_exit(&device->device_lock);
1367                 } else {
1368                         DBG("heci_poll: iamthif no event\n");
1369                         *reventsp = 0;
1370                         if (!anyyet)
1371                                 *phpp = &device->iamthif_file_ext.pollwait;
1372                 }
1373                 mutex_exit(&device->iamthif_file_ext.file_lock);
1374 
1375         } else {
1376                 mutex_enter(&file_extension->write_io_lock);
1377                 if (HECI_WRITE_COMPLETE == file_extension->writing_state) {
1378                         *reventsp |= (POLLIN | POLLRDNORM);
1379                         DBG("heci_poll: file_extension poll event\n");
1380                 } else {
1381                         DBG("heci_poll: file_extension no event\n");
1382                         *reventsp = 0;
1383                         if (!anyyet)
1384                                 *phpp = &file_extension->tx_pollwait;
1385                 }
1386                 mutex_exit(&file_extension->write_io_lock);
1387 
1388 
1389         }
1390 
1391         return (0);
1392 }
1393 
1394 /*
1395  * heci_fe_same_id - tell if file private data have same id
1396  *
1397  * @fe1: private data of 1. file object
1398  * @fe2: private data of 2. file object
1399  *
1400  * @return  !=0 - if ids are the same, 0 - if differ.
1401  */
1402 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1403                 struct heci_file_private *fe2)
1404 {
1405         return ((fe1->host_client_id == fe2->host_client_id) &&
1406             (fe1->me_client_id == fe2->me_client_id));
1407 }
1408 
1409 /*
1410  * Since the ME firmware won't reset itself during OS reboot, it's not enough
1411  * to only disable interrupts in quiesce(), here we do a full hand-shake
1412  * with the firmware.
1413  */
1414 static int
1415 heci_quiesce(dev_info_t *dip)
1416 {
1417         struct iamt_heci_device *dev;
1418 
1419         dev = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1420         ASSERT(dev != NULL);
1421 
1422         if (dev->wd_file_ext.state == HECI_FILE_CONNECTED &&
1423             dev->wd_timeout) {
1424                 dev->wd_timeout = 0;
1425                 dev->wd_due_counter = 0;
1426                 (void) memcpy(dev->wd_data, stop_wd_params,
1427                     HECI_WD_PARAMS_SIZE);
1428                 if (!heci_send_wd(dev)) {
1429                         DBG("send stop WD failed\n");
1430                 }
1431 
1432         }
1433 
1434         /* disable interrupts */
1435         heci_csr_disable_interrupts(dev);
1436 
1437         return (DDI_SUCCESS);
1438 }
1439 
1440 static int
1441 heci_suspend(dev_info_t *dip)
1442 {
1443         struct iamt_heci_device *device;
1444         int err = 0;
1445 
1446         device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1447 
1448         if (device->reinit_tsk)
1449                 ddi_taskq_wait(device->reinit_tsk);
1450 
1451         /* Stop watchdog if exists */
1452         if (device->wd_timer)
1453                 (void) untimeout(device->wd_timer);
1454 
1455         mutex_enter(&device->device_lock);
1456 
1457         if (device->wd_file_ext.state == HECI_FILE_CONNECTED &&
1458             device->wd_timeout) {
1459                 g_sus_wd_timeout = device->wd_timeout;
1460                 device->wd_timeout = 0;
1461                 device->wd_due_counter = 0;
1462                 (void) memcpy(device->wd_data, stop_wd_params,
1463                     HECI_WD_PARAMS_SIZE);
1464                 device->stop = 1;
1465                 if (device->host_buffer_is_empty &&
1466                     flow_ctrl_creds(device, &device->wd_file_ext)) {
1467                         device->host_buffer_is_empty = 0;
1468                         if (!heci_send_wd(device)) {
1469                                 DBG("send stop WD failed\n");
1470                         }
1471                         else
1472                                 flow_ctrl_reduce(device, &device->wd_file_ext);
1473 
1474                         device->wd_pending = 0;
1475                 } else {
1476                         device->wd_pending = 1;
1477                 }
1478                 device->wd_stoped = 0;
1479 
1480                 err = 0;
1481                 while (!device->wd_stoped && err != -1) {
1482                         err = cv_reltimedwait(&device->wait_stop_wd,
1483                             &device->device_lock, 10*HZ, TR_CLOCK_TICK);
1484                 }
1485 
1486                 if (!device->wd_stoped) {
1487                         DBG("stop wd failed to complete.\n");
1488                 } else {
1489                         DBG("stop wd complete %d.\n", err);
1490                         err = 0;
1491                 }
1492         }
1493         /* Set new heci state */
1494         if (device->heci_state == HECI_ENABLED ||
1495             device->heci_state == HECI_RECOVERING_FROM_RESET) {
1496                 device->heci_state = HECI_POWER_DOWN;
1497                 heci_reset(device, 0);
1498         }
1499 
1500         /* Here interrupts are already disabled by heci_reset() */
1501 
1502         mutex_exit(&device->device_lock);
1503 
1504 
1505         return (err);
1506 }
1507 
1508 static void
1509 heci_resume(dev_info_t *dip)
1510 {
1511         struct iamt_heci_device *device;
1512 
1513         device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip));
1514 
1515         mutex_enter(&device->device_lock);
1516         device->heci_state = HECI_POWER_UP;
1517         heci_reset(device, 1);
1518         mutex_exit(&device->device_lock);
1519 
1520         /* Start watchdog if stopped in suspend */
1521         if (g_sus_wd_timeout != 0) {
1522                 device->wd_timeout = g_sus_wd_timeout;
1523 
1524                 (void) memcpy(device->wd_data, start_wd_params,
1525                     HECI_WD_PARAMS_SIZE);
1526                 (void) memcpy(device->wd_data + HECI_WD_PARAMS_SIZE,
1527                     &device->wd_timeout, sizeof (uint16_t));
1528                 device->wd_due_counter = 1;
1529 
1530                 if (device->wd_timeout)
1531                         device->wd_timer = timeout(heci_wd_timer, device, 1);
1532 
1533                 g_sus_wd_timeout = 0;
1534         }
1535 }