1 /*
   2  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 
   6 /*
   7  * Part of Intel(R) Manageability Engine Interface Linux driver
   8  *
   9  * Copyright (c) 2003 - 2008 Intel Corp.
  10  * All rights reserved.
  11  *
  12  * Redistribution and use in source and binary forms, with or without
  13  * modification, are permitted provided that the following conditions
  14  * are met:
  15  * 1. Redistributions of source code must retain the above copyright
  16  *    notice, this list of conditions, and the following disclaimer,
  17  *    without modification.
  18  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
  19  *    substantially similar to the "NO WARRANTY" disclaimer below
  20  *    ("Disclaimer") and any redistribution must be conditioned upon
  21  *    including a substantially similar Disclaimer requirement for further
  22  *    binary redistribution.
  23  * 3. Neither the names of the above-listed copyright holders nor the names
  24  *    of any contributors may be used to endorse or promote products derived
  25  *    from this software without specific prior written permission.
  26  *
  27  * Alternatively, this software may be distributed under the terms of the
  28  * GNU General Public License ("GPL") version 2 as published by the Free
  29  * Software Foundation.
  30  *
  31  * NO WARRANTY
  32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
  35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  36  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
  41  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  42  * POSSIBILITY OF SUCH DAMAGES.
  43  *
  44  */
  45 
  46 #include <sys/types.h>
  47 #include <sys/cmn_err.h>
  48 #include <sys/conf.h>
  49 #include <sys/ddi.h>
  50 #include <sys/ddi_impldefs.h>
  51 #include <sys/devops.h>
  52 #include <sys/instance.h>
  53 #include <sys/modctl.h>
  54 #include <sys/open.h>
  55 #include <sys/stat.h>
  56 #include <sys/sunddi.h>
  57 #include <sys/sunndi.h>
  58 #include <sys/systm.h>
  59 #include <sys/mkdev.h>
  60 #include <sys/list.h>
  61 #include <sys/note.h>
  62 #include "heci_data_structures.h"
  63 #include "heci_interface.h"
  64 #include "heci.h"
  65 
  66 
  67 const uint8_t watch_dog_data[] = {
  68         1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
  69 };
  70 const uint8_t start_wd_params[] = { 0x02, 0x12, 0x13, 0x10 };
  71 const uint8_t stop_wd_params[] = { 0x02, 0x02, 0x14, 0x10 };
  72 
  73 const uint8_t heci_wd_state_independence_msg[3][4] = {
  74         {0x05, 0x02, 0x51, 0x10},
  75         {0x05, 0x02, 0x52, 0x10},
  76         {0x07, 0x02, 0x01, 0x10}
  77 };
  78 
  79 const struct guid heci_asf_guid = {
  80         0x75B30CD6, 0xA29E, 0x4AF7,
  81         {0xA7, 0x12, 0xE6, 0x17, 0x43, 0x93, 0xC8, 0xA6}
  82 };
  83 const struct guid heci_wd_guid = {
  84         0x05B79A6F, 0x4628, 0x4D7F,
  85         {0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB}
  86 };
  87 const struct guid heci_pthi_guid = {
  88         0x12f80028, 0xb4b7, 0x4b2d,
  89         {0xac, 0xa8, 0x46, 0xe0, 0xff, 0x65, 0x81, 0x4c}
  90 };
  91 
  92 
  93 /*
  94  *  heci init function prototypes
  95  */
  96 static void heci_check_asf_mode(struct iamt_heci_device *dev);
  97 static int host_start_message(struct iamt_heci_device *dev);
  98 static int host_enum_clients_message(struct iamt_heci_device *dev);
  99 static int allocate_me_clients_storage(struct iamt_heci_device *dev);
 100 static void host_init_wd(struct iamt_heci_device *dev);
 101 static void host_init_iamthif(struct iamt_heci_device *dev);
 102 static inline int heci_fe_same_id(struct heci_file_private *fe1,
 103                 struct heci_file_private *fe2);
 104 
 105 
 106 
 107 /*
 108  * heci_initialize_list - Sets up a  queue  list.
 109  *
 110  * @list: An instance of our list structure
 111  * @dev: Device object for our driver
 112  */
 113 void
 114 heci_initialize_list(struct io_heci_list *list,
 115         struct iamt_heci_device *dev)
 116 {
 117         /* initialize our queue list */
 118         LIST_INIT_HEAD(&list->heci_cb.cb_list);
 119         list->status = 0;
 120         list->device_extension = dev;
 121 }
 122 
 123 /*
 124  * heci_flush_queues - flush our queues list belong to file_ext.
 125  *
 126  * @dev: Device object for our driver
 127  * @file_ext: private data of the file object
 128  *
 129  */
 130 void
 131 heci_flush_queues(struct iamt_heci_device *dev,
 132         struct heci_file_private *file_ext)
 133 {
 134         int i;
 135 
 136         if (!dev || !file_ext)
 137                 return;
 138 
 139         /* flush our queue list belong to file_ext */
 140         for (i = 0; i < HECI_IO_LISTS_NUMBER; i++) {
 141                 DBG("remove list entry belong to file_ext\n");
 142                 heci_flush_list(dev->io_list_array[i], file_ext);
 143         }
 144 }
 145 
 146 
 147 /*
 148  * heci_flush_list - remove list entry belong to file_ext.
 149  *
 150  * @list:  An instance of our list structure
 151  * @file_ext: private data of the file object
 152  */
 153 void
 154 heci_flush_list(struct io_heci_list *list,
 155         struct heci_file_private *file_ext)
 156 {
 157         struct heci_file_private *file_ext_tmp;
 158         struct heci_cb_private *priv_cb_pos = NULL;
 159         struct heci_cb_private *priv_cb_next = NULL;
 160 
 161         if (!list || !file_ext)
 162                 return;
 163 
 164         if (list->status != 0)
 165                 return;
 166 
 167         if (list_empty(&list->heci_cb.cb_list))
 168                 return;
 169 
 170         list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
 171                 &list->heci_cb.cb_list, cb_list, struct heci_cb_private) {
 172                 if (priv_cb_pos) {
 173                         file_ext_tmp = (struct heci_file_private *)
 174                                 priv_cb_pos->file_private;
 175                         if (file_ext_tmp) {
 176                                 if (heci_fe_same_id(file_ext, file_ext_tmp))
 177                                         list_del(&priv_cb_pos->cb_list);
 178                         }
 179                 }
 180         }
 181 }
 182 
 183 /*
 184  * heci_reset_iamthif_params - initializes heci device iamthif
 185  * @dev: The heci device structure
 186  */
 187 static void heci_reset_iamthif_params(struct iamt_heci_device *dev)
 188 {
 189         /* reset iamthif parameters. */
 190         dev->iamthif_current_cb = NULL;
 191         dev->iamthif_msg_buf_size = 0;
 192         dev->iamthif_msg_buf_index = 0;
 193         dev->iamthif_canceled = 0;
 194         dev->iamthif_file_ext.file = NULL;
 195         dev->iamthif_ioctl = 0;
 196         dev->iamthif_state = HECI_IAMTHIF_IDLE;
 197         dev->iamthif_timer = 0;
 198 }
 199 
 200 /*
 201  * fini_heci_device - release resources allocated in init_heci_device()
 202  */
 203 void
 204 fini_heci_device(struct iamt_heci_device *device)
 205 {
 206         mutex_destroy(&device->device_lock);
 207         cv_destroy(&device->wait_recvd_msg);
 208         cv_destroy(&device->wait_stop_wd);
 209         if (device->work)
 210                 ddi_taskq_destroy(device->work);
 211         if (device->reinit_tsk)
 212                 ddi_taskq_destroy(device->reinit_tsk);
 213 
 214 }
 215 
 216 /*
 217  * init_heci_device - initializes the heci device structure
 218  *
 219  */
 220 void
 221 init_heci_device(dev_info_t *dip,
 222         struct iamt_heci_device *device)
 223 {
 224         int i;
 225 
 226         if (!device)
 227                 return;
 228 
 229         /* setup our list array */
 230         device->io_list_array[0] = &device->read_list;
 231         device->io_list_array[1] = &device->write_list;
 232         device->io_list_array[2] = &device->write_waiting_list;
 233         device->io_list_array[3] = &device->ctrl_wr_list;
 234         device->io_list_array[4] = &device->ctrl_rd_list;
 235         device->io_list_array[5] = &device->pthi_cmd_list;
 236         device->io_list_array[6] = &device->pthi_read_complete_list;
 237         LIST_INIT_HEAD(&device->file_list);
 238         LIST_INIT_HEAD(&device->wd_file_ext.link);
 239         LIST_INIT_HEAD(&device->iamthif_file_ext.link);
 240         mutex_init(&device->device_lock, NULL, MUTEX_DRIVER, NULL);
 241         cv_init(&device->wait_recvd_msg, NULL, CV_DRIVER, NULL);
 242         cv_init(&device->wait_stop_wd, NULL, CV_DRIVER, NULL);
 243         device->open_handle_count = 0;
 244         device->num_heci_me_clients = 0;
 245         device->extra_write_index = 0;
 246         device->rd_msg_hdr = 0;
 247         device->mem_addr = NULL;
 248         device->asf_mode = B_FALSE;
 249         device->need_reset = B_FALSE;
 250         device->recvd_msg = B_FALSE;
 251         device->heci_state = HECI_INITIALIZING;
 252         device->iamthif_state = HECI_IAMTHIF_IDLE;
 253 
 254         device->work = ddi_taskq_create(dip, "heci_bh_handler", 1,
 255             TASKQ_DEFAULTPRI, 0);
 256         if (device->work == NULL)
 257                 cmn_err(CE_WARN, "taskq_create failed for heci_bh_handler");
 258         device->reinit_tsk = ddi_taskq_create(dip, "heci_reinit_tsk", 1,
 259             TASKQ_DEFAULTPRI, 0);
 260         if (device->reinit_tsk == NULL)
 261                 cmn_err(CE_WARN, "taskq_create failed for reinit_tsk");
 262 
 263         device->wd_pending = B_FALSE;
 264         device->wd_stoped = B_FALSE;
 265 
 266         device->me_clients = NULL;
 267         for (i = 0; i < HECI_IO_LISTS_NUMBER; i++)
 268                 heci_initialize_list(device->io_list_array[i], device);
 269         device->dip = dip;
 270 }
 271 
 272 /*
 273  * heci_hw_init  - init host and fw to start work.
 274  *
 275  * @dev: Device object for our driver
 276  *
 277  * @return 0 on success, <0 on failure.
 278  */
 279 int
 280 heci_hw_init(struct iamt_heci_device *dev)
 281 {
 282         int err = 0;
 283 
 284         mutex_enter(&dev->device_lock);
 285         dev->host_hw_state = read_heci_register(dev, H_CSR);
 286         dev->me_hw_state = read_heci_register(dev, ME_CSR_HA);
 287         DBG("host_hw_state = 0x%08x, mestate = 0x%08x.\n",
 288             dev->host_hw_state, dev->me_hw_state);
 289 
 290         if ((dev->host_hw_state & H_IS) == H_IS) {
 291                 /* acknowledge interrupt and stop interupts */
 292                 heci_set_csr_register(dev);
 293         }
 294         dev->recvd_msg = 0;
 295         DBG("reset in start the heci device.\n");
 296 
 297         heci_reset(dev, 1);
 298 
 299         DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
 300             dev->host_hw_state, dev->me_hw_state);
 301 
 302         /* wait for ME to turn on ME_RDY */
 303         err = 0;
 304         while (!dev->recvd_msg && err != -1) {
 305                 err = cv_reltimedwait(&dev->wait_recvd_msg,
 306                     &dev->device_lock, HECI_INTEROP_TIMEOUT, TR_CLOCK_TICK);
 307         }
 308 
 309         if (err == -1 && !dev->recvd_msg) {
 310                 dev->heci_state = HECI_DISABLED;
 311                 DBG("wait_event_interruptible_timeout failed"
 312                     "on wait for ME to turn on ME_RDY.\n");
 313                 mutex_exit(&dev->device_lock);
 314                 return (-ENODEV);
 315         } else {
 316                 if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
 317                     ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
 318                         dev->heci_state = HECI_DISABLED;
 319                         DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
 320                             dev->host_hw_state,
 321                             dev->me_hw_state);
 322 
 323                         if (!(dev->host_hw_state & H_RDY) != H_RDY)
 324                                 DBG("host turn off H_RDY.\n");
 325 
 326                         if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA)
 327                                 DBG("ME turn off ME_RDY.\n");
 328 
 329                         cmn_err(CE_WARN,
 330                             "heci: link layer initialization failed.\n");
 331                         mutex_exit(&dev->device_lock);
 332                         return (-ENODEV);
 333                 }
 334         }
 335         dev->recvd_msg = 0;
 336         DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
 337             dev->host_hw_state, dev->me_hw_state);
 338         DBG("ME turn on ME_RDY and host turn on H_RDY.\n");
 339         DBG("heci: link layer has been established.\n");
 340         mutex_exit(&dev->device_lock);
 341         return (0);
 342 }
 343 
 344 /*
 345  * heci_hw_reset  - reset fw via heci csr register.
 346  *
 347  * @dev: Device object for our driver
 348  * @interrupts: if interrupt should be enable after reset.
 349  */
 350 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts)
 351 {
 352         dev->host_hw_state |= (H_RST | H_IG);
 353 
 354         if (interrupts)
 355                 heci_csr_enable_interrupts(dev);
 356         else
 357                 heci_csr_disable_interrupts(dev);
 358 
 359 }
 360 
 361 /*
 362  * heci_reset  - reset host and fw.
 363  *
 364  * @dev: Device object for our driver
 365  * @interrupts: if interrupt should be enable after reset.
 366  */
 367 void
 368 heci_reset(struct iamt_heci_device *dev, int interrupts)
 369 {
 370         struct heci_file_private *file_pos = NULL;
 371         struct heci_file_private *file_next = NULL;
 372         struct heci_cb_private *priv_cb_pos = NULL;
 373         struct heci_cb_private *priv_cb_next = NULL;
 374         int unexpected = 0;
 375 
 376         if (dev->heci_state == HECI_RECOVERING_FROM_RESET) {
 377                 dev->need_reset = 1;
 378                 return;
 379         }
 380 
 381         if (dev->heci_state != HECI_INITIALIZING &&
 382             dev->heci_state != HECI_DISABLED &&
 383             dev->heci_state != HECI_POWER_DOWN &&
 384             dev->heci_state != HECI_POWER_UP)
 385                 unexpected = 1;
 386 
 387         if (dev->reinit_tsk != NULL) {
 388                 mutex_exit(&dev->device_lock);
 389                 (void) ddi_taskq_wait(dev->reinit_tsk);
 390                 mutex_enter(&dev->device_lock);
 391         }
 392 
 393         dev->host_hw_state = read_heci_register(dev, H_CSR);
 394 
 395         DBG("before reset host_hw_state = 0x%08x.\n",
 396             dev->host_hw_state);
 397 
 398         heci_hw_reset(dev, interrupts);
 399 
 400         dev->host_hw_state &= ~H_RST;
 401         dev->host_hw_state |= H_IG;
 402 
 403         write_heci_register(dev, H_CSR, dev->host_hw_state);
 404 
 405         DBG("currently saved host_hw_state = 0x%08x.\n",
 406             dev->host_hw_state);
 407 
 408         dev->need_reset = 0;
 409 
 410         if (dev->heci_state != HECI_INITIALIZING) {
 411                 if ((dev->heci_state != HECI_DISABLED) &&
 412                     (dev->heci_state != HECI_POWER_DOWN))
 413                         dev->heci_state = HECI_RESETING;
 414 
 415                 list_for_each_entry_safe(file_pos,
 416                     file_next, &dev->file_list, link,
 417                     struct heci_file_private) {
 418                         file_pos->state = HECI_FILE_DISCONNECTED;
 419                         file_pos->flow_ctrl_creds = 0;
 420                         file_pos->read_cb = NULL;
 421                         file_pos->timer_count = 0;
 422                 }
 423                 /* remove entry if already in list */
 424                 DBG("list del iamthif and wd file list.\n");
 425                 heci_remove_client_from_file_list(dev,
 426                     dev->wd_file_ext.host_client_id);
 427 
 428                 heci_remove_client_from_file_list(dev,
 429                     dev->iamthif_file_ext.host_client_id);
 430 
 431                 heci_reset_iamthif_params(dev);
 432                 dev->wd_due_counter = 0;
 433                 dev->extra_write_index = 0;
 434         }
 435 
 436         dev->num_heci_me_clients = 0;
 437         dev->rd_msg_hdr = 0;
 438         dev->stop = 0;
 439         dev->wd_pending = 0;
 440 
 441         /* update the state of the registers after reset */
 442         dev->host_hw_state =  read_heci_register(dev, H_CSR);
 443         dev->me_hw_state =  read_heci_register(dev, ME_CSR_HA);
 444 
 445         DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
 446             dev->host_hw_state, dev->me_hw_state);
 447 
 448         if (unexpected)
 449                 cmn_err(CE_WARN, "unexpected heci reset.\n");
 450 
 451         /* Wake up all readings so they can be interrupted */
 452         list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
 453             struct heci_file_private) {
 454                 cmn_err(CE_NOTE, "heci: Waking up client!\n");
 455                 cv_broadcast(&file_pos->rx_wait);
 456         }
 457         /* remove all waiting requests */
 458         if (dev->write_list.status == 0 &&
 459             !list_empty(&dev->write_list.heci_cb.cb_list)) {
 460                 list_for_each_entry_safe(priv_cb_pos, priv_cb_next,
 461                     &dev->write_list.heci_cb.cb_list, cb_list,
 462                     struct heci_cb_private) {
 463                         if (priv_cb_pos) {
 464                                 list_del(&priv_cb_pos->cb_list);
 465                                 heci_free_cb_private(priv_cb_pos);
 466                         }
 467                 }
 468         }
 469 }
 470 
 471 /*
 472  * heci_initialize_clients  -  routine.
 473  *
 474  * @dev: Device object for our driver
 475  *
 476  */
 477 int
 478 heci_initialize_clients(struct iamt_heci_device *dev)
 479 {
 480         int status;
 481 
 482         /* msleep(100) FW needs time to be ready to talk with us */
 483         delay(drv_usectohz(100000));
 484         DBG("link is established start sending messages.\n");
 485         /* link is established start sending messages. */
 486         status = host_start_message(dev);
 487         if (status != 0) {
 488                 mutex_enter(&dev->device_lock);
 489                 dev->heci_state = HECI_DISABLED;
 490                 mutex_exit(&dev->device_lock);
 491                 DBG("start sending messages failed.\n");
 492                 return (status);
 493         }
 494         /* enumerate clients */
 495 
 496         status = host_enum_clients_message(dev);
 497         if (status != 0) {
 498                 mutex_enter(&dev->device_lock);
 499                 dev->heci_state = HECI_DISABLED;
 500                 mutex_exit(&dev->device_lock);
 501                 DBG("enum clients failed.\n");
 502                 return (status);
 503         }
 504         /* allocate storage for ME clients representation */
 505         status = allocate_me_clients_storage(dev);
 506         if (status != 0) {
 507                 mutex_enter(&dev->device_lock);
 508                 dev->num_heci_me_clients = 0;
 509                 dev->heci_state = HECI_DISABLED;
 510                 mutex_exit(&dev->device_lock);
 511                 DBG("allocate clients failed.\n");
 512                 return (status);
 513         }
 514 
 515         heci_check_asf_mode(dev);
 516         /* heci initialization wd */
 517         host_init_wd(dev);
 518         /* heci initialization iamthif client */
 519         host_init_iamthif(dev);
 520 
 521         mutex_enter(&dev->device_lock);
 522         if (dev->need_reset) {
 523                 dev->need_reset = 0;
 524                 dev->heci_state = HECI_DISABLED;
 525                 mutex_exit(&dev->device_lock);
 526                 return (-ENODEV);
 527         }
 528 
 529         (void) memset(dev->heci_host_clients, 0,
 530             sizeof (dev->heci_host_clients));
 531         dev->open_handle_count = 0;
 532         dev->heci_host_clients[0] |= 7;
 533         dev->current_host_client_id = 3;
 534         dev->heci_state = HECI_ENABLED;
 535         mutex_exit(&dev->device_lock);
 536         DBG("initialization heci clients successful.\n");
 537         return (0);
 538 }
 539 
 540 /*
 541  * heci_task_initialize_clients  -  routine.
 542  *
 543  * @data: Device object for our driver
 544  *
 545  */
 546 void
 547 heci_task_initialize_clients(void *data)
 548 {
 549         int ret;
 550         struct iamt_heci_device *dev = (struct iamt_heci_device *)data;
 551 
 552         ret = heci_initialize_clients(dev);
 553         if (ret)
 554                 cmn_err(CE_WARN, "heci_initialize_clients() failed\n");
 555 }
 556 
 557 /*
 558  * host_start_message - heci host send start message.
 559  *
 560  * @dev: Device object for our driver
 561  *
 562  * @return 0 on success, <0 on failure.
 563  */
 564 static int
 565 host_start_message(struct iamt_heci_device *dev)
 566 {
 567         long timeout = 60;      /* 60 second */
 568         struct heci_msg_hdr *heci_hdr;
 569         struct hbm_host_version_request *host_start_req;
 570         struct hbm_host_stop_request *host_stop_req;
 571         int err = 0;
 572         clock_t delta = (clock_t)(timeout * HZ);
 573 
 574         /* host start message */
 575         mutex_enter(&dev->device_lock);
 576         heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
 577         heci_hdr->host_addr = 0;
 578         heci_hdr->me_addr = 0;
 579         heci_hdr->length = sizeof (struct hbm_host_version_request);
 580         heci_hdr->msg_complete = 1;
 581         heci_hdr->reserved = 0;
 582 
 583         host_start_req =
 584             (struct hbm_host_version_request *)&dev->wr_msg_buf[1];
 585         (void) memset(host_start_req, 0,
 586             sizeof (struct hbm_host_version_request));
 587         host_start_req->cmd.cmd = HOST_START_REQ_CMD;
 588         host_start_req->host_version.major_version = HBM_MAJOR_VERSION;
 589         host_start_req->host_version.minor_version = HBM_MINOR_VERSION;
 590         dev->recvd_msg = 0;
 591         if (!heci_write_message(dev, heci_hdr,
 592             (unsigned char *)(host_start_req),
 593             heci_hdr->length)) {
 594                 DBG("send version to fw fail.\n");
 595                 mutex_exit(&dev->device_lock);
 596                 return (-ENODEV);
 597         }
 598         DBG("call wait_event_interruptible_timeout for response message.\n");
 599         err = 0;
 600         while (err != -1 && !dev->recvd_msg) {
 601                 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
 602                     delta, TR_CLOCK_TICK);
 603         }
 604         if (err == -1 && !dev->recvd_msg) {
 605                 DBG("wait_timeout failed on host start response message.\n");
 606                 mutex_exit(&dev->device_lock);
 607                 return (-ENODEV);
 608         }
 609         dev->recvd_msg = 0;
 610         DBG("wait_timeout successful on host start response message.\n");
 611         if ((dev->version.major_version != HBM_MAJOR_VERSION) ||
 612             (dev->version.minor_version != HBM_MINOR_VERSION)) {
 613                 /* send stop message */
 614                 heci_hdr->host_addr = 0;
 615                 heci_hdr->me_addr = 0;
 616                 heci_hdr->length = sizeof (struct hbm_host_stop_request);
 617                 heci_hdr->msg_complete = 1;
 618                 heci_hdr->reserved = 0;
 619 
 620                 host_stop_req =
 621                     (struct hbm_host_stop_request *)&dev->wr_msg_buf[1];
 622 
 623                 (void) memset(host_stop_req, 0,
 624                     sizeof (struct hbm_host_stop_request));
 625                 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD;
 626                 host_stop_req->reason = DRIVER_STOP_REQUEST;
 627                 if (!heci_write_message(dev, heci_hdr,
 628                     (unsigned char *)(host_stop_req),
 629                     heci_hdr->length)) {
 630                         DBG("sending stop msg to fw failed.\n");
 631                 }
 632                 DBG("version mismatch.\n");
 633                 mutex_exit(&dev->device_lock);
 634                 return (-ENODEV);
 635         }
 636         mutex_exit(&dev->device_lock);
 637         return (0);
 638 }
 639 
 640 /*
 641  * host_enum_clients_message - host send enumeration client request message.
 642  *
 643  * @dev: Device object for our driver
 644  * @return 0 on success, <0 on failure.
 645  */
 646 static int
 647 host_enum_clients_message(struct iamt_heci_device *dev)
 648 {
 649         long timeout = 5;       /* 5 second */
 650         struct heci_msg_hdr *heci_hdr;
 651         struct hbm_host_enum_request *host_enum_req;
 652         int err = 0;
 653         uint8_t i, j;
 654         clock_t delta = (clock_t)(timeout * HZ);
 655 
 656         mutex_enter(&dev->device_lock);
 657 
 658         heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
 659         /* enumerate clients */
 660         heci_hdr->host_addr = 0;
 661         heci_hdr->me_addr = 0;
 662         heci_hdr->length = sizeof (struct hbm_host_enum_request);
 663         heci_hdr->msg_complete = 1;
 664         heci_hdr->reserved = 0;
 665 
 666         host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1];
 667         (void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request));
 668         host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD;
 669         if (!heci_write_message(dev, heci_hdr,
 670             (unsigned char *)(host_enum_req),
 671             heci_hdr->length)) {
 672                 DBG("send enumeration request failed.\n");
 673                 mutex_exit(&dev->device_lock);
 674                 return (-ENODEV);
 675         }
 676         /* wait for response */
 677         dev->recvd_msg = 0;
 678         err = 0;
 679         while (!dev->recvd_msg && err != -1) {
 680                 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
 681                     delta, TR_CLOCK_TICK);
 682         }
 683         if (err == -1 && !dev->recvd_msg) {
 684                 DBG("wait_event_interruptible_timeout failed "
 685                 "on enumeration clients response message.\n");
 686                 mutex_exit(&dev->device_lock);
 687                 return (-ENODEV);
 688         }
 689         dev->recvd_msg = 0;
 690 
 691         /* count how many ME clients we have */
 692         for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
 693                 for (j = 0; j < 8; j++) {
 694                         if ((dev->heci_me_clients[i] & (1 << j)) != 0)
 695                                 dev->num_heci_me_clients++;
 696 
 697                 }
 698         }
 699         mutex_exit(&dev->device_lock);
 700 
 701         return (0);
 702 }
 703 
 704 /*
 705  * host_client_properties - reads properties for client
 706  *
 707  * @dev: Device object for our driver
 708  * @idx: client index in me client array
 709  * @client_id: id of the client
 710  *
 711  * @return 0 on success, <0 on failure.
 712  */
 713 static int
 714 host_client_properties(struct iamt_heci_device *dev,
 715         struct heci_me_client *client)
 716 {
 717         struct heci_msg_hdr *heci_hdr;
 718         struct hbm_props_request *host_cli_req;
 719         int err;
 720         clock_t delta = 10 * HZ;
 721 
 722         mutex_enter(&dev->device_lock);
 723         heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0];
 724         heci_hdr->host_addr = 0;
 725         heci_hdr->me_addr = 0;
 726         heci_hdr->length = sizeof (struct hbm_props_request);
 727         heci_hdr->msg_complete = 1;
 728         heci_hdr->reserved = 0;
 729 
 730         host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1];
 731         (void) memset(host_cli_req, 0, sizeof (struct hbm_props_request));
 732         host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD;
 733         host_cli_req->address = client->client_id;
 734         if (!heci_write_message(dev, heci_hdr,
 735             (unsigned char *)(host_cli_req), heci_hdr->length)) {
 736                 DBG("send props request failed.\n");
 737                 mutex_exit(&dev->device_lock);
 738                 return (-ENODEV);
 739         }
 740         /* wait for response */
 741         dev->recvd_msg = 0;
 742 
 743         err = 0;
 744         while (!dev->recvd_msg && err != -1) {
 745                 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
 746                     delta, TR_CLOCK_TICK);
 747         }
 748         if (err == -1 && !dev->recvd_msg) {
 749                 DBG("wait failed on props resp msg.\n");
 750                 mutex_exit(&dev->device_lock);
 751                 return (-ENODEV);
 752         }
 753         dev->recvd_msg = 0;
 754         mutex_exit(&dev->device_lock);
 755         return (0);
 756 }
 757 
 758 /*
 759  * allocate_me_clients_storage - allocate storage for me clients
 760  *
 761  * @dev: Device object for our driver
 762  *
 763  * @return 0 on success, <0 on failure.
 764  */
 765 static int
 766 allocate_me_clients_storage(struct iamt_heci_device *dev)
 767 {
 768         struct heci_me_client *clients;
 769         struct heci_me_client *client;
 770         uint8_t num, i, j;
 771         int err;
 772 
 773         if (dev->num_heci_me_clients == 0)
 774                 return (0);
 775 
 776         mutex_enter(&dev->device_lock);
 777         if (dev->me_clients) {
 778                 kmem_free(dev->me_clients, dev->num_heci_me_clients*
 779                     sizeof (struct heci_me_client));
 780                 dev->me_clients = NULL;
 781         }
 782         mutex_exit(&dev->device_lock);
 783 
 784         /* allocate storage for ME clients representation */
 785         clients = kmem_zalloc(dev->num_heci_me_clients*
 786             sizeof (struct heci_me_client), KM_SLEEP);
 787         if (!clients) {
 788                 DBG("memory allocation for ME clients failed.\n");
 789                 return (-ENOMEM);
 790         }
 791 
 792         mutex_enter(&dev->device_lock);
 793         dev->me_clients = clients;
 794         mutex_exit(&dev->device_lock);
 795 
 796         num = 0;
 797         for (i = 0; i < sizeof (dev->heci_me_clients); i++) {
 798                 for (j = 0; j < 8; j++) {
 799                         if ((dev->heci_me_clients[i] & (1 << j)) != 0) {
 800                                 client = &dev->me_clients[num];
 801                                 client->client_id = (i * 8) + j;
 802                                 client->flow_ctrl_creds = 0;
 803                                 err = host_client_properties(dev, client);
 804                                 if (err != 0) {
 805                                         mutex_enter(&dev->device_lock);
 806                                         kmem_free(dev->me_clients,
 807                                             dev->num_heci_me_clients*
 808                                             sizeof (struct heci_me_client));
 809                                         dev->me_clients = NULL;
 810                                         mutex_exit(&dev->device_lock);
 811                                         return (err);
 812                                 }
 813                                 num++;
 814                         }
 815                 }
 816         }
 817 
 818         return (0);
 819 }
 820 
 821 /*
 822  * heci_init_file_private - initializes private file structure.
 823  *
 824  * @priv: private file structure to be initialized
 825  * @file: the file structure
 826  *
 827  */
 828 static void
 829 heci_init_file_private(struct heci_file_private *priv,
 830         struct heci_file *file)
 831 {
 832         _NOTE(ARGUNUSED(file));
 833 
 834         (void) memset(priv, 0, sizeof (struct heci_file_private));
 835         mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL);
 836         mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL);
 837         mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL);
 838         cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL);
 839         DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait);
 840         LIST_INIT_HEAD(&priv->link);
 841         priv->reading_state = HECI_IDLE;
 842         priv->writing_state = HECI_IDLE;
 843 }
 844 
 845 /*
 846  * heci_find_me_client - search for ME client guid
 847  *                       sets client_id in heci_file_private if found
 848  * @dev: Device object for our driver
 849  * @priv: private file structure to set client_id in
 850  * @cguid: searched guid of ME client
 851  * @client_id: id of host client to be set in file private structure
 852  *
 853  * @return ME client index
 854  */
 855 static uint8_t
 856 heci_find_me_client(struct iamt_heci_device *dev,
 857                                 struct heci_file_private *priv,
 858                                 const struct guid *cguid, uint8_t client_id)
 859 {
 860         uint8_t i;
 861 
 862         if ((dev == NULL) || (priv == NULL) || (cguid == NULL))
 863                 return (0);
 864 
 865         for (i = 0; i < dev->num_heci_me_clients; i++) {
 866                 if (memcmp(cguid,
 867                     &dev->me_clients[i].props.protocol_name,
 868                     sizeof (struct guid)) == 0) {
 869                         priv->me_client_id = dev->me_clients[i].client_id;
 870                         priv->state = HECI_FILE_CONNECTING;
 871                         priv->host_client_id = client_id;
 872 
 873                         list_add_tail(&priv->link, &dev->file_list);
 874                         return (i);
 875                 }
 876         }
 877         return (0);
 878 }
 879 
 880 /*
 881  * heci_check_asf_mode - check for ASF client
 882  *
 883  * @dev: Device object for our driver
 884  *
 885  */
 886 static void
 887 heci_check_asf_mode(struct iamt_heci_device *dev)
 888 {
 889         uint8_t i;
 890 
 891         mutex_enter(&dev->device_lock);
 892         dev->asf_mode = 0;
 893         /* find ME ASF client - otherwise assume AMT mode */
 894         DBG("find ME ASF client - otherwise assume AMT mode.\n");
 895         for (i = 0; i < dev->num_heci_me_clients; i++) {
 896                 if (memcmp(&heci_asf_guid,
 897                     &dev->me_clients[i].props.protocol_name,
 898                     sizeof (struct guid)) == 0) {
 899                         dev->asf_mode = 1;
 900                         mutex_exit(&dev->device_lock);
 901                         DBG("found ME ASF client.\n");
 902                         return;
 903                 }
 904         }
 905         mutex_exit(&dev->device_lock);
 906         DBG("assume AMT mode.\n");
 907 }
 908 
 909 /*
 910  * heci_connect_me_client - connect ME client
 911  * @dev: Device object for our driver
 912  * @priv: private file structure
 913  * @timeout: connect timeout in seconds
 914  *
 915  * @return 1 - if connected, 0 - if not
 916  */
 917 static uint8_t
 918 heci_connect_me_client(struct iamt_heci_device *dev,
 919         struct heci_file_private *priv,
 920         long timeout)
 921 {
 922         int err = 0;
 923         clock_t delta = (clock_t)(timeout * HZ);
 924 
 925         if ((dev == NULL) || (priv == NULL))
 926                 return (0);
 927 
 928         if (!heci_connect(dev, priv)) {
 929                 DBG("failed to call heci_connect for client_id=%d.\n",
 930                     priv->host_client_id);
 931                 heci_remove_client_from_file_list(dev, priv->host_client_id);
 932                 priv->state = HECI_FILE_DISCONNECTED;
 933                 return (0);
 934         }
 935         err = 0;
 936         while (!(HECI_FILE_CONNECTED == priv->state ||
 937             HECI_FILE_DISCONNECTED == priv->state) &&
 938             err != -1) {
 939                 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
 940                     delta, TR_CLOCK_TICK);
 941         }
 942         if (HECI_FILE_CONNECTED != priv->state) {
 943                 heci_remove_client_from_file_list(dev, priv->host_client_id);
 944                 DBG("failed to connect client_id=%d state=%d.\n",
 945                     priv->host_client_id, priv->state);
 946                 if (err)
 947                         DBG("failed connect err=%08x\n", err);
 948                 priv->state = HECI_FILE_DISCONNECTED;
 949                 return (0);
 950         }
 951         DBG("successfully connected client_id=%d.\n",
 952             priv->host_client_id);
 953         return (1);
 954 }
 955 
 956 /*
 957  * host_init_wd - heci initialization wd.
 958  *
 959  * @dev: Device object for our driver
 960  *
 961  */
 962 static void host_init_wd(struct iamt_heci_device *dev)
 963 {
 964 
 965         mutex_enter(&dev->device_lock);
 966 
 967         heci_init_file_private(&dev->wd_file_ext, NULL);
 968 
 969         /* look for WD client and connect to it */
 970         dev->wd_file_ext.state = HECI_FILE_DISCONNECTED;
 971         dev->wd_timeout = 0;
 972 
 973         if (dev->asf_mode) {
 974                 (void) memcpy(dev->wd_data, stop_wd_params,
 975                     HECI_WD_PARAMS_SIZE);
 976         } else {
 977                 /* AMT mode */
 978                 dev->wd_timeout = AMT_WD_VALUE;
 979                 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
 980                 (void) memcpy(dev->wd_data, start_wd_params,
 981                     HECI_WD_PARAMS_SIZE);
 982                 (void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE,
 983                     &dev->wd_timeout, sizeof (uint16_t));
 984         }
 985 
 986         /* find ME WD client */
 987         (void) heci_find_me_client(dev, &dev->wd_file_ext,
 988             &heci_wd_guid, HECI_WD_HOST_CLIENT_ID);
 989 
 990         DBG("check wd_file_ext\n");
 991         if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) {
 992                 if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) {
 993                         DBG("dev->wd_timeout=%d.\n", dev->wd_timeout);
 994                         if (dev->wd_timeout != 0)
 995                                 dev->wd_due_counter = 1;
 996                         else
 997                                 dev->wd_due_counter = 0;
 998                         DBG("successfully connected to WD client.\n");
 999                 }
1000         } else
1001                 DBG("failed to find WD client.\n");
1002 
1003 
1004         mutex_exit(&dev->device_lock);
1005 }
1006 
1007 
1008 /*
1009  * host_init_iamthif - heci initialization iamthif client.
1010  *
1011  * @dev: Device object for our driver
1012  *
1013  */
1014 static void
1015 host_init_iamthif(struct iamt_heci_device *dev)
1016 {
1017         uint8_t i;
1018 
1019         mutex_enter(&dev->device_lock);
1020 
1021         heci_init_file_private(&dev->iamthif_file_ext, NULL);
1022         dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED;
1023 
1024         /* find ME PTHI client */
1025         i = heci_find_me_client(dev, &dev->iamthif_file_ext,
1026             &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID);
1027         if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) {
1028                 DBG("failed to find iamthif client.\n");
1029                 mutex_exit(&dev->device_lock);
1030                 return;
1031         }
1032 
1033         ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU);
1034 
1035         if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) {
1036                 DBG("connected to iamthif client.\n");
1037                 dev->iamthif_state = HECI_IAMTHIF_IDLE;
1038         }
1039         mutex_exit(&dev->device_lock);
1040 }
1041 
1042 /*
1043  * heci_alloc_file_private - allocates a private file structure and set it up.
1044  * @file: the file structure
1045  *
1046  * @return  The allocated file or NULL on failure
1047  */
1048 struct heci_file_private *
1049 heci_alloc_file_private(struct heci_file *file)
1050 {
1051         struct heci_file_private *priv;
1052 
1053         priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP);
1054         if (!priv)
1055                 return (NULL);
1056 
1057         heci_init_file_private(priv, file);
1058 
1059         return (priv);
1060 }
1061 
1062 /*
1063  * heci_free_file_private - free a private file structure that were previously
1064  * allocated by heci_alloc_file_private
1065  */
1066 void
1067 heci_free_file_private(struct heci_file_private *priv)
1068 {
1069         mutex_destroy(&priv->file_lock);
1070         mutex_destroy(&priv->read_io_lock);
1071         mutex_destroy(&priv->write_io_lock);
1072         cv_destroy(&priv->rx_wait);
1073         kmem_free(priv, sizeof (struct heci_file_private));
1074 
1075 }
1076 
1077 /*
1078  * heci_disconnect_host_client  - send disconnect message  to fw from host
1079  * client.
1080  *
1081  * @dev: Device object for our driver
1082  * @file_ext: private data of the file object
1083  *
1084  * @return 0 on success, <0 on failure.
1085  */
1086 int
1087 heci_disconnect_host_client(struct iamt_heci_device *dev,
1088                 struct heci_file_private *file_ext)
1089 {
1090         int rets, err;
1091         long timeout = 15;      /* 15 seconds */
1092         struct heci_cb_private *priv_cb;
1093         clock_t delta = (clock_t)(timeout * HZ);
1094 
1095         if ((!dev) || (!file_ext))
1096                 return (-ENODEV);
1097 
1098         if (file_ext->state != HECI_FILE_DISCONNECTING)
1099                 return (0);
1100 
1101         priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP);
1102         if (!priv_cb)
1103                 return (-ENOMEM);
1104 
1105         LIST_INIT_HEAD(&priv_cb->cb_list);
1106         priv_cb->file_private = file_ext;
1107         priv_cb->major_file_operations = HECI_CLOSE;
1108         mutex_enter(&dev->device_lock);
1109         if (dev->host_buffer_is_empty) {
1110                 dev->host_buffer_is_empty = 0;
1111                 if (heci_disconnect(dev, file_ext)) {
1112                         list_add_tail(&priv_cb->cb_list,
1113                             &dev->ctrl_rd_list.heci_cb.cb_list);
1114                 } else {
1115                         mutex_exit(&dev->device_lock);
1116                         rets = -ENODEV;
1117                         DBG("failed to call heci_disconnect.\n");
1118                         goto free;
1119                 }
1120         } else {
1121                 DBG("add disconnect cb to control write list\n");
1122                 list_add_tail(&priv_cb->cb_list,
1123                     &dev->ctrl_wr_list.heci_cb.cb_list);
1124         }
1125 
1126         err = 0;
1127         while (err != -1 &&
1128             (HECI_FILE_DISCONNECTED != file_ext->state)) {
1129 
1130                 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock,
1131                     delta, TR_CLOCK_TICK);
1132         }
1133         mutex_exit(&dev->device_lock);
1134 
1135         if (HECI_FILE_DISCONNECTED == file_ext->state) {
1136                 rets = 0;
1137                 DBG("successfully disconnected from fw client."
1138                     " me_client_id:%d, host_client_id:%d\n",
1139                     file_ext->me_client_id,
1140                     file_ext->host_client_id);
1141         } else {
1142                 rets = -ENODEV;
1143                 if (HECI_FILE_DISCONNECTED != file_ext->state)
1144                         DBG("wrong status client disconnect.\n");
1145 
1146                 if (err)
1147                         DBG("wait failed disconnect err=%08x\n", err);
1148 
1149                 DBG("failed to disconnect from fw client.\n"
1150                     " me_client_id:%d, host_client_id:%d\n",
1151                     file_ext->me_client_id,
1152                     file_ext->host_client_id);
1153         }
1154 
1155         mutex_enter(&dev->device_lock);
1156         heci_flush_list(&dev->ctrl_rd_list, file_ext);
1157         heci_flush_list(&dev->ctrl_wr_list, file_ext);
1158         mutex_exit(&dev->device_lock);
1159 free:
1160         heci_free_cb_private(priv_cb);
1161         return (rets);
1162 }
1163 
1164 /*
1165  * heci_remove_client_from_file_list  -
1166  *      remove file private data from device file list
1167  *
1168  * @dev: Device object for our driver
1169  * @host_client_id: host client id to be removed
1170  *
1171  */
1172 void
1173 heci_remove_client_from_file_list(struct iamt_heci_device *dev,
1174         uint8_t host_client_id)
1175 {
1176         struct heci_file_private *file_pos = NULL;
1177         struct heci_file_private *file_next = NULL;
1178         list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link,
1179                 struct heci_file_private) {
1180                 if (host_client_id == file_pos->host_client_id) {
1181                         DBG("remove host client = %d, ME client = %d\n",
1182                                         file_pos->host_client_id,
1183                                         file_pos->me_client_id);
1184                         list_del_init(&file_pos->link);
1185                         break;
1186                 }
1187         }
1188 }
1189 
1190 /*
1191  * heci_fe_same_id - tell if file private data have same id
1192  *
1193  * @fe1: private data of 1. file object
1194  * @fe2: private data of 2. file object
1195  *
1196  * @return  !=0 - if ids are the same, 0 - if differ.
1197  */
1198 static inline int heci_fe_same_id(struct heci_file_private *fe1,
1199                 struct heci_file_private *fe2)
1200 {
1201         return ((fe1->host_client_id == fe2->host_client_id) &&
1202             (fe1->me_client_id == fe2->me_client_id));
1203 }