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 clock_t tm; 306 tm = ddi_get_lbolt(); 307 err = cv_timedwait(&dev->wait_recvd_msg, 308 &dev->device_lock, 309 tm + HECI_INTEROP_TIMEOUT); 310 } 311 312 if (err == -1 && !dev->recvd_msg) { 313 dev->heci_state = HECI_DISABLED; 314 DBG("wait_event_interruptible_timeout failed" 315 "on wait for ME to turn on ME_RDY.\n"); 316 mutex_exit(&dev->device_lock); 317 return (-ENODEV); 318 } else { 319 if (!(((dev->host_hw_state & H_RDY) == H_RDY) && 320 ((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) { 321 dev->heci_state = HECI_DISABLED; 322 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", 323 dev->host_hw_state, 324 dev->me_hw_state); 325 326 if (!(dev->host_hw_state & H_RDY) != H_RDY) 327 DBG("host turn off H_RDY.\n"); 328 329 if (!(dev->me_hw_state & ME_RDY_HRA) != ME_RDY_HRA) 330 DBG("ME turn off ME_RDY.\n"); 331 332 cmn_err(CE_WARN, 333 "heci: link layer initialization failed.\n"); 334 mutex_exit(&dev->device_lock); 335 return (-ENODEV); 336 } 337 } 338 dev->recvd_msg = 0; 339 DBG("host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", 340 dev->host_hw_state, dev->me_hw_state); 341 DBG("ME turn on ME_RDY and host turn on H_RDY.\n"); 342 DBG("heci: link layer has been established.\n"); 343 mutex_exit(&dev->device_lock); 344 return (0); 345 } 346 347 /* 348 * heci_hw_reset - reset fw via heci csr register. 349 * 350 * @dev: Device object for our driver 351 * @interrupts: if interrupt should be enable after reset. 352 */ 353 static void heci_hw_reset(struct iamt_heci_device *dev, int interrupts) 354 { 355 dev->host_hw_state |= (H_RST | H_IG); 356 357 if (interrupts) 358 heci_csr_enable_interrupts(dev); 359 else 360 heci_csr_disable_interrupts(dev); 361 362 } 363 364 /* 365 * heci_reset - reset host and fw. 366 * 367 * @dev: Device object for our driver 368 * @interrupts: if interrupt should be enable after reset. 369 */ 370 void 371 heci_reset(struct iamt_heci_device *dev, int interrupts) 372 { 373 struct heci_file_private *file_pos = NULL; 374 struct heci_file_private *file_next = NULL; 375 struct heci_cb_private *priv_cb_pos = NULL; 376 struct heci_cb_private *priv_cb_next = NULL; 377 int unexpected = 0; 378 379 if (dev->heci_state == HECI_RECOVERING_FROM_RESET) { 380 dev->need_reset = 1; 381 return; 382 } 383 384 if (dev->heci_state != HECI_INITIALIZING && 385 dev->heci_state != HECI_DISABLED && 386 dev->heci_state != HECI_POWER_DOWN && 387 dev->heci_state != HECI_POWER_UP) 388 unexpected = 1; 389 390 if (dev->reinit_tsk != NULL) { 391 mutex_exit(&dev->device_lock); 392 (void) ddi_taskq_wait(dev->reinit_tsk); 393 mutex_enter(&dev->device_lock); 394 } 395 396 dev->host_hw_state = read_heci_register(dev, H_CSR); 397 398 DBG("before reset host_hw_state = 0x%08x.\n", 399 dev->host_hw_state); 400 401 heci_hw_reset(dev, interrupts); 402 403 dev->host_hw_state &= ~H_RST; 404 dev->host_hw_state |= H_IG; 405 406 write_heci_register(dev, H_CSR, dev->host_hw_state); 407 408 DBG("currently saved host_hw_state = 0x%08x.\n", 409 dev->host_hw_state); 410 411 dev->need_reset = 0; 412 413 if (dev->heci_state != HECI_INITIALIZING) { 414 if ((dev->heci_state != HECI_DISABLED) && 415 (dev->heci_state != HECI_POWER_DOWN)) 416 dev->heci_state = HECI_RESETING; 417 418 list_for_each_entry_safe(file_pos, 419 file_next, &dev->file_list, link, 420 struct heci_file_private) { 421 file_pos->state = HECI_FILE_DISCONNECTED; 422 file_pos->flow_ctrl_creds = 0; 423 file_pos->read_cb = NULL; 424 file_pos->timer_count = 0; 425 } 426 /* remove entry if already in list */ 427 DBG("list del iamthif and wd file list.\n"); 428 heci_remove_client_from_file_list(dev, 429 dev->wd_file_ext.host_client_id); 430 431 heci_remove_client_from_file_list(dev, 432 dev->iamthif_file_ext.host_client_id); 433 434 heci_reset_iamthif_params(dev); 435 dev->wd_due_counter = 0; 436 dev->extra_write_index = 0; 437 } 438 439 dev->num_heci_me_clients = 0; 440 dev->rd_msg_hdr = 0; 441 dev->stop = 0; 442 dev->wd_pending = 0; 443 444 /* update the state of the registers after reset */ 445 dev->host_hw_state = read_heci_register(dev, H_CSR); 446 dev->me_hw_state = read_heci_register(dev, ME_CSR_HA); 447 448 DBG("after reset host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n", 449 dev->host_hw_state, dev->me_hw_state); 450 451 if (unexpected) 452 cmn_err(CE_WARN, "unexpected heci reset.\n"); 453 454 /* Wake up all readings so they can be interrupted */ 455 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link, 456 struct heci_file_private) { 457 cmn_err(CE_NOTE, "heci: Waking up client!\n"); 458 cv_broadcast(&file_pos->rx_wait); 459 } 460 /* remove all waiting requests */ 461 if (dev->write_list.status == 0 && 462 !list_empty(&dev->write_list.heci_cb.cb_list)) { 463 list_for_each_entry_safe(priv_cb_pos, priv_cb_next, 464 &dev->write_list.heci_cb.cb_list, cb_list, 465 struct heci_cb_private) { 466 if (priv_cb_pos) { 467 list_del(&priv_cb_pos->cb_list); 468 heci_free_cb_private(priv_cb_pos); 469 } 470 } 471 } 472 } 473 474 /* 475 * heci_initialize_clients - routine. 476 * 477 * @dev: Device object for our driver 478 * 479 */ 480 int 481 heci_initialize_clients(struct iamt_heci_device *dev) 482 { 483 int status; 484 485 /* msleep(100) FW needs time to be ready to talk with us */ 486 delay(drv_usectohz(100000)); 487 DBG("link is established start sending messages.\n"); 488 /* link is established start sending messages. */ 489 status = host_start_message(dev); 490 if (status != 0) { 491 mutex_enter(&dev->device_lock); 492 dev->heci_state = HECI_DISABLED; 493 mutex_exit(&dev->device_lock); 494 DBG("start sending messages failed.\n"); 495 return (status); 496 } 497 /* enumerate clients */ 498 499 status = host_enum_clients_message(dev); 500 if (status != 0) { 501 mutex_enter(&dev->device_lock); 502 dev->heci_state = HECI_DISABLED; 503 mutex_exit(&dev->device_lock); 504 DBG("enum clients failed.\n"); 505 return (status); 506 } 507 /* allocate storage for ME clients representation */ 508 status = allocate_me_clients_storage(dev); 509 if (status != 0) { 510 mutex_enter(&dev->device_lock); 511 dev->num_heci_me_clients = 0; 512 dev->heci_state = HECI_DISABLED; 513 mutex_exit(&dev->device_lock); 514 DBG("allocate clients failed.\n"); 515 return (status); 516 } 517 518 heci_check_asf_mode(dev); 519 /* heci initialization wd */ 520 host_init_wd(dev); 521 /* heci initialization iamthif client */ 522 host_init_iamthif(dev); 523 524 mutex_enter(&dev->device_lock); 525 if (dev->need_reset) { 526 dev->need_reset = 0; 527 dev->heci_state = HECI_DISABLED; 528 mutex_exit(&dev->device_lock); 529 return (-ENODEV); 530 } 531 532 (void) memset(dev->heci_host_clients, 0, 533 sizeof (dev->heci_host_clients)); 534 dev->open_handle_count = 0; 535 dev->heci_host_clients[0] |= 7; 536 dev->current_host_client_id = 3; 537 dev->heci_state = HECI_ENABLED; 538 mutex_exit(&dev->device_lock); 539 DBG("initialization heci clients successful.\n"); 540 return (0); 541 } 542 543 /* 544 * heci_task_initialize_clients - routine. 545 * 546 * @data: Device object for our driver 547 * 548 */ 549 void 550 heci_task_initialize_clients(void *data) 551 { 552 int ret; 553 struct iamt_heci_device *dev = (struct iamt_heci_device *)data; 554 555 ret = heci_initialize_clients(dev); 556 if (ret) 557 cmn_err(CE_WARN, "heci_initialize_clients() failed\n"); 558 } 559 560 /* 561 * host_start_message - heci host send start message. 562 * 563 * @dev: Device object for our driver 564 * 565 * @return 0 on success, <0 on failure. 566 */ 567 static int 568 host_start_message(struct iamt_heci_device *dev) 569 { 570 long timeout = 60; /* 60 second */ 571 struct heci_msg_hdr *heci_hdr; 572 struct hbm_host_version_request *host_start_req; 573 struct hbm_host_stop_request *host_stop_req; 574 int err = 0; 575 576 /* host start message */ 577 mutex_enter(&dev->device_lock); 578 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 579 heci_hdr->host_addr = 0; 580 heci_hdr->me_addr = 0; 581 heci_hdr->length = sizeof (struct hbm_host_version_request); 582 heci_hdr->msg_complete = 1; 583 heci_hdr->reserved = 0; 584 585 host_start_req = 586 (struct hbm_host_version_request *)&dev->wr_msg_buf[1]; 587 (void) memset(host_start_req, 0, 588 sizeof (struct hbm_host_version_request)); 589 host_start_req->cmd.cmd = HOST_START_REQ_CMD; 590 host_start_req->host_version.major_version = HBM_MAJOR_VERSION; 591 host_start_req->host_version.minor_version = HBM_MINOR_VERSION; 592 dev->recvd_msg = 0; 593 if (!heci_write_message(dev, heci_hdr, 594 (unsigned char *)(host_start_req), 595 heci_hdr->length)) { 596 DBG("send version to fw fail.\n"); 597 mutex_exit(&dev->device_lock); 598 return (-ENODEV); 599 } 600 DBG("call wait_event_interruptible_timeout for response message.\n"); 601 err = 0; 602 while (err != -1 && !dev->recvd_msg) { 603 clock_t tm; 604 tm = ddi_get_lbolt(); 605 err = cv_timedwait(&dev->wait_recvd_msg, 606 &dev->device_lock, tm + timeout * HZ); 607 } 608 if (err == -1 && !dev->recvd_msg) { 609 DBG("wait_timeout failed on host start response message.\n"); 610 mutex_exit(&dev->device_lock); 611 return (-ENODEV); 612 } 613 dev->recvd_msg = 0; 614 DBG("wait_timeout successful on host start response message.\n"); 615 if ((dev->version.major_version != HBM_MAJOR_VERSION) || 616 (dev->version.minor_version != HBM_MINOR_VERSION)) { 617 /* send stop message */ 618 heci_hdr->host_addr = 0; 619 heci_hdr->me_addr = 0; 620 heci_hdr->length = sizeof (struct hbm_host_stop_request); 621 heci_hdr->msg_complete = 1; 622 heci_hdr->reserved = 0; 623 624 host_stop_req = 625 (struct hbm_host_stop_request *)&dev->wr_msg_buf[1]; 626 627 (void) memset(host_stop_req, 0, 628 sizeof (struct hbm_host_stop_request)); 629 host_stop_req->cmd.cmd = HOST_STOP_REQ_CMD; 630 host_stop_req->reason = DRIVER_STOP_REQUEST; 631 if (!heci_write_message(dev, heci_hdr, 632 (unsigned char *)(host_stop_req), 633 heci_hdr->length)) { 634 DBG("sending stop msg to fw failed.\n"); 635 } 636 DBG("version mismatch.\n"); 637 mutex_exit(&dev->device_lock); 638 return (-ENODEV); 639 } 640 mutex_exit(&dev->device_lock); 641 return (0); 642 } 643 644 /* 645 * host_enum_clients_message - host send enumeration client request message. 646 * 647 * @dev: Device object for our driver 648 * @return 0 on success, <0 on failure. 649 */ 650 static int 651 host_enum_clients_message(struct iamt_heci_device *dev) 652 { 653 long timeout = 5; /* 5 second */ 654 struct heci_msg_hdr *heci_hdr; 655 struct hbm_host_enum_request *host_enum_req; 656 int err = 0; 657 uint8_t i, j; 658 659 mutex_enter(&dev->device_lock); 660 661 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 662 /* enumerate clients */ 663 heci_hdr->host_addr = 0; 664 heci_hdr->me_addr = 0; 665 heci_hdr->length = sizeof (struct hbm_host_enum_request); 666 heci_hdr->msg_complete = 1; 667 heci_hdr->reserved = 0; 668 669 host_enum_req = (struct hbm_host_enum_request *)&dev->wr_msg_buf[1]; 670 (void) memset(host_enum_req, 0, sizeof (struct hbm_host_enum_request)); 671 host_enum_req->cmd.cmd = HOST_ENUM_REQ_CMD; 672 if (!heci_write_message(dev, heci_hdr, 673 (unsigned char *)(host_enum_req), 674 heci_hdr->length)) { 675 DBG("send enumeration request failed.\n"); 676 mutex_exit(&dev->device_lock); 677 return (-ENODEV); 678 } 679 /* wait for response */ 680 dev->recvd_msg = 0; 681 err = 0; 682 while (!dev->recvd_msg && err != -1) { 683 clock_t tm; 684 tm = ddi_get_lbolt(); 685 err = cv_timedwait(&dev->wait_recvd_msg, 686 &dev->device_lock, 687 tm + timeout * HZ); 688 } 689 if (err == -1 && !dev->recvd_msg) { 690 DBG("wait_event_interruptible_timeout failed " 691 "on enumeration clients response message.\n"); 692 mutex_exit(&dev->device_lock); 693 return (-ENODEV); 694 } 695 dev->recvd_msg = 0; 696 697 /* count how many ME clients we have */ 698 for (i = 0; i < sizeof (dev->heci_me_clients); i++) { 699 for (j = 0; j < 8; j++) { 700 if ((dev->heci_me_clients[i] & (1 << j)) != 0) 701 dev->num_heci_me_clients++; 702 703 } 704 } 705 mutex_exit(&dev->device_lock); 706 707 return (0); 708 } 709 710 /* 711 * host_client_properties - reads properties for client 712 * 713 * @dev: Device object for our driver 714 * @idx: client index in me client array 715 * @client_id: id of the client 716 * 717 * @return 0 on success, <0 on failure. 718 */ 719 static int 720 host_client_properties(struct iamt_heci_device *dev, 721 struct heci_me_client *client) 722 { 723 struct heci_msg_hdr *heci_hdr; 724 struct hbm_props_request *host_cli_req; 725 int err; 726 727 mutex_enter(&dev->device_lock); 728 heci_hdr = (struct heci_msg_hdr *)&dev->wr_msg_buf[0]; 729 heci_hdr->host_addr = 0; 730 heci_hdr->me_addr = 0; 731 heci_hdr->length = sizeof (struct hbm_props_request); 732 heci_hdr->msg_complete = 1; 733 heci_hdr->reserved = 0; 734 735 host_cli_req = (struct hbm_props_request *)&dev->wr_msg_buf[1]; 736 (void) memset(host_cli_req, 0, sizeof (struct hbm_props_request)); 737 host_cli_req->cmd.cmd = HOST_CLIENT_PROPERTEIS_REQ_CMD; 738 host_cli_req->address = client->client_id; 739 if (!heci_write_message(dev, heci_hdr, 740 (unsigned char *)(host_cli_req), heci_hdr->length)) { 741 DBG("send props request failed.\n"); 742 mutex_exit(&dev->device_lock); 743 return (-ENODEV); 744 } 745 /* wait for response */ 746 dev->recvd_msg = 0; 747 748 err = 0; 749 while (!dev->recvd_msg && err != -1) { 750 clock_t tm; 751 tm = ddi_get_lbolt(); 752 err = cv_timedwait(&dev->wait_recvd_msg, 753 &dev->device_lock, 754 tm + 10 * HZ); 755 } 756 if (err == -1 && !dev->recvd_msg) { 757 DBG("wait failed on props resp msg.\n"); 758 mutex_exit(&dev->device_lock); 759 return (-ENODEV); 760 } 761 dev->recvd_msg = 0; 762 mutex_exit(&dev->device_lock); 763 return (0); 764 } 765 766 /* 767 * allocate_me_clients_storage - allocate storage for me clients 768 * 769 * @dev: Device object for our driver 770 * 771 * @return 0 on success, <0 on failure. 772 */ 773 static int 774 allocate_me_clients_storage(struct iamt_heci_device *dev) 775 { 776 struct heci_me_client *clients; 777 struct heci_me_client *client; 778 uint8_t num, i, j; 779 int err; 780 781 if (dev->num_heci_me_clients == 0) 782 return (0); 783 784 mutex_enter(&dev->device_lock); 785 if (dev->me_clients) { 786 kmem_free(dev->me_clients, dev->num_heci_me_clients* 787 sizeof (struct heci_me_client)); 788 dev->me_clients = NULL; 789 } 790 mutex_exit(&dev->device_lock); 791 792 /* allocate storage for ME clients representation */ 793 clients = kmem_zalloc(dev->num_heci_me_clients* 794 sizeof (struct heci_me_client), KM_SLEEP); 795 if (!clients) { 796 DBG("memory allocation for ME clients failed.\n"); 797 return (-ENOMEM); 798 } 799 800 mutex_enter(&dev->device_lock); 801 dev->me_clients = clients; 802 mutex_exit(&dev->device_lock); 803 804 num = 0; 805 for (i = 0; i < sizeof (dev->heci_me_clients); i++) { 806 for (j = 0; j < 8; j++) { 807 if ((dev->heci_me_clients[i] & (1 << j)) != 0) { 808 client = &dev->me_clients[num]; 809 client->client_id = (i * 8) + j; 810 client->flow_ctrl_creds = 0; 811 err = host_client_properties(dev, client); 812 if (err != 0) { 813 mutex_enter(&dev->device_lock); 814 kmem_free(dev->me_clients, 815 dev->num_heci_me_clients* 816 sizeof (struct heci_me_client)); 817 dev->me_clients = NULL; 818 mutex_exit(&dev->device_lock); 819 return (err); 820 } 821 num++; 822 } 823 } 824 } 825 826 return (0); 827 } 828 829 /* 830 * heci_init_file_private - initializes private file structure. 831 * 832 * @priv: private file structure to be initialized 833 * @file: the file structure 834 * 835 */ 836 static void 837 heci_init_file_private(struct heci_file_private *priv, 838 struct heci_file *file) 839 { 840 _NOTE(ARGUNUSED(file)); 841 842 (void) memset(priv, 0, sizeof (struct heci_file_private)); 843 mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL); 844 mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL); 845 mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL); 846 cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL); 847 DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait); 848 LIST_INIT_HEAD(&priv->link); 849 priv->reading_state = HECI_IDLE; 850 priv->writing_state = HECI_IDLE; 851 } 852 853 /* 854 * heci_find_me_client - search for ME client guid 855 * sets client_id in heci_file_private if found 856 * @dev: Device object for our driver 857 * @priv: private file structure to set client_id in 858 * @cguid: searched guid of ME client 859 * @client_id: id of host client to be set in file private structure 860 * 861 * @return ME client index 862 */ 863 static uint8_t 864 heci_find_me_client(struct iamt_heci_device *dev, 865 struct heci_file_private *priv, 866 const struct guid *cguid, uint8_t client_id) 867 { 868 uint8_t i; 869 870 if ((dev == NULL) || (priv == NULL) || (cguid == NULL)) 871 return (0); 872 873 for (i = 0; i < dev->num_heci_me_clients; i++) { 874 if (memcmp(cguid, 875 &dev->me_clients[i].props.protocol_name, 876 sizeof (struct guid)) == 0) { 877 priv->me_client_id = dev->me_clients[i].client_id; 878 priv->state = HECI_FILE_CONNECTING; 879 priv->host_client_id = client_id; 880 881 list_add_tail(&priv->link, &dev->file_list); 882 return (i); 883 } 884 } 885 return (0); 886 } 887 888 /* 889 * heci_check_asf_mode - check for ASF client 890 * 891 * @dev: Device object for our driver 892 * 893 */ 894 static void 895 heci_check_asf_mode(struct iamt_heci_device *dev) 896 { 897 uint8_t i; 898 899 mutex_enter(&dev->device_lock); 900 dev->asf_mode = 0; 901 /* find ME ASF client - otherwise assume AMT mode */ 902 DBG("find ME ASF client - otherwise assume AMT mode.\n"); 903 for (i = 0; i < dev->num_heci_me_clients; i++) { 904 if (memcmp(&heci_asf_guid, 905 &dev->me_clients[i].props.protocol_name, 906 sizeof (struct guid)) == 0) { 907 dev->asf_mode = 1; 908 mutex_exit(&dev->device_lock); 909 DBG("found ME ASF client.\n"); 910 return; 911 } 912 } 913 mutex_exit(&dev->device_lock); 914 DBG("assume AMT mode.\n"); 915 } 916 917 /* 918 * heci_connect_me_client - connect ME client 919 * @dev: Device object for our driver 920 * @priv: private file structure 921 * @timeout: connect timeout in seconds 922 * 923 * @return 1 - if connected, 0 - if not 924 */ 925 static uint8_t 926 heci_connect_me_client(struct iamt_heci_device *dev, 927 struct heci_file_private *priv, 928 long timeout) 929 { 930 int err = 0; 931 932 if ((dev == NULL) || (priv == NULL)) 933 return (0); 934 935 if (!heci_connect(dev, priv)) { 936 DBG("failed to call heci_connect for client_id=%d.\n", 937 priv->host_client_id); 938 heci_remove_client_from_file_list(dev, priv->host_client_id); 939 priv->state = HECI_FILE_DISCONNECTED; 940 return (0); 941 } 942 err = 0; 943 while (!(HECI_FILE_CONNECTED == priv->state || 944 HECI_FILE_DISCONNECTED == priv->state) && 945 err != -1) { 946 clock_t tm; 947 tm = ddi_get_lbolt(); 948 err = cv_timedwait(&dev->wait_recvd_msg, 949 &dev->device_lock, 950 tm + timeout*HZ); 951 } 952 if (HECI_FILE_CONNECTED != priv->state) { 953 heci_remove_client_from_file_list(dev, priv->host_client_id); 954 DBG("failed to connect client_id=%d state=%d.\n", 955 priv->host_client_id, priv->state); 956 if (err) 957 DBG("failed connect err=%08x\n", err); 958 priv->state = HECI_FILE_DISCONNECTED; 959 return (0); 960 } 961 DBG("successfully connected client_id=%d.\n", 962 priv->host_client_id); 963 return (1); 964 } 965 966 /* 967 * host_init_wd - heci initialization wd. 968 * 969 * @dev: Device object for our driver 970 * 971 */ 972 static void host_init_wd(struct iamt_heci_device *dev) 973 { 974 975 mutex_enter(&dev->device_lock); 976 977 heci_init_file_private(&dev->wd_file_ext, NULL); 978 979 /* look for WD client and connect to it */ 980 dev->wd_file_ext.state = HECI_FILE_DISCONNECTED; 981 dev->wd_timeout = 0; 982 983 if (dev->asf_mode) { 984 (void) memcpy(dev->wd_data, stop_wd_params, 985 HECI_WD_PARAMS_SIZE); 986 } else { 987 /* AMT mode */ 988 dev->wd_timeout = AMT_WD_VALUE; 989 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout); 990 (void) memcpy(dev->wd_data, start_wd_params, 991 HECI_WD_PARAMS_SIZE); 992 (void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE, 993 &dev->wd_timeout, sizeof (uint16_t)); 994 } 995 996 /* find ME WD client */ 997 (void) heci_find_me_client(dev, &dev->wd_file_ext, 998 &heci_wd_guid, HECI_WD_HOST_CLIENT_ID); 999 1000 DBG("check wd_file_ext\n"); 1001 if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) { 1002 if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) { 1003 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout); 1004 if (dev->wd_timeout != 0) 1005 dev->wd_due_counter = 1; 1006 else 1007 dev->wd_due_counter = 0; 1008 DBG("successfully connected to WD client.\n"); 1009 } 1010 } else 1011 DBG("failed to find WD client.\n"); 1012 1013 1014 mutex_exit(&dev->device_lock); 1015 } 1016 1017 1018 /* 1019 * host_init_iamthif - heci initialization iamthif client. 1020 * 1021 * @dev: Device object for our driver 1022 * 1023 */ 1024 static void 1025 host_init_iamthif(struct iamt_heci_device *dev) 1026 { 1027 uint8_t i; 1028 1029 mutex_enter(&dev->device_lock); 1030 1031 heci_init_file_private(&dev->iamthif_file_ext, NULL); 1032 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED; 1033 1034 /* find ME PTHI client */ 1035 i = heci_find_me_client(dev, &dev->iamthif_file_ext, 1036 &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID); 1037 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) { 1038 DBG("failed to find iamthif client.\n"); 1039 mutex_exit(&dev->device_lock); 1040 return; 1041 } 1042 1043 ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU); 1044 1045 if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) { 1046 DBG("connected to iamthif client.\n"); 1047 dev->iamthif_state = HECI_IAMTHIF_IDLE; 1048 } 1049 mutex_exit(&dev->device_lock); 1050 } 1051 1052 /* 1053 * heci_alloc_file_private - allocates a private file structure and set it up. 1054 * @file: the file structure 1055 * 1056 * @return The allocated file or NULL on failure 1057 */ 1058 struct heci_file_private * 1059 heci_alloc_file_private(struct heci_file *file) 1060 { 1061 struct heci_file_private *priv; 1062 1063 priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP); 1064 if (!priv) 1065 return (NULL); 1066 1067 heci_init_file_private(priv, file); 1068 1069 return (priv); 1070 } 1071 1072 /* 1073 * heci_free_file_private - free a private file structure that were previously 1074 * allocated by heci_alloc_file_private 1075 */ 1076 void 1077 heci_free_file_private(struct heci_file_private *priv) 1078 { 1079 mutex_destroy(&priv->file_lock); 1080 mutex_destroy(&priv->read_io_lock); 1081 mutex_destroy(&priv->write_io_lock); 1082 cv_destroy(&priv->rx_wait); 1083 kmem_free(priv, sizeof (struct heci_file_private)); 1084 1085 } 1086 1087 /* 1088 * heci_disconnect_host_client - send disconnect message to fw from host 1089 * client. 1090 * 1091 * @dev: Device object for our driver 1092 * @file_ext: private data of the file object 1093 * 1094 * @return 0 on success, <0 on failure. 1095 */ 1096 int 1097 heci_disconnect_host_client(struct iamt_heci_device *dev, 1098 struct heci_file_private *file_ext) 1099 { 1100 int rets, err; 1101 long timeout = 15; /* 15 seconds */ 1102 struct heci_cb_private *priv_cb; 1103 1104 if ((!dev) || (!file_ext)) 1105 return (-ENODEV); 1106 1107 if (file_ext->state != HECI_FILE_DISCONNECTING) 1108 return (0); 1109 1110 priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP); 1111 if (!priv_cb) 1112 return (-ENOMEM); 1113 1114 LIST_INIT_HEAD(&priv_cb->cb_list); 1115 priv_cb->file_private = file_ext; 1116 priv_cb->major_file_operations = HECI_CLOSE; 1117 mutex_enter(&dev->device_lock); 1118 if (dev->host_buffer_is_empty) { 1119 dev->host_buffer_is_empty = 0; 1120 if (heci_disconnect(dev, file_ext)) { 1121 list_add_tail(&priv_cb->cb_list, 1122 &dev->ctrl_rd_list.heci_cb.cb_list); 1123 } else { 1124 mutex_exit(&dev->device_lock); 1125 rets = -ENODEV; 1126 DBG("failed to call heci_disconnect.\n"); 1127 goto free; 1128 } 1129 } else { 1130 DBG("add disconnect cb to control write list\n"); 1131 list_add_tail(&priv_cb->cb_list, 1132 &dev->ctrl_wr_list.heci_cb.cb_list); 1133 } 1134 1135 err = 0; 1136 while (err != -1 && 1137 (HECI_FILE_DISCONNECTED != file_ext->state)) { 1138 1139 clock_t tm; 1140 tm = ddi_get_lbolt(); 1141 err = cv_timedwait(&dev->wait_recvd_msg, 1142 &dev->device_lock, 1143 tm + timeout * HZ); 1144 } 1145 mutex_exit(&dev->device_lock); 1146 1147 if (HECI_FILE_DISCONNECTED == file_ext->state) { 1148 rets = 0; 1149 DBG("successfully disconnected from fw client." 1150 " me_client_id:%d, host_client_id:%d\n", 1151 file_ext->me_client_id, 1152 file_ext->host_client_id); 1153 } else { 1154 rets = -ENODEV; 1155 if (HECI_FILE_DISCONNECTED != file_ext->state) 1156 DBG("wrong status client disconnect.\n"); 1157 1158 if (err) 1159 DBG("wait failed disconnect err=%08x\n", err); 1160 1161 DBG("failed to disconnect from fw client.\n" 1162 " me_client_id:%d, host_client_id:%d\n", 1163 file_ext->me_client_id, 1164 file_ext->host_client_id); 1165 } 1166 1167 mutex_enter(&dev->device_lock); 1168 heci_flush_list(&dev->ctrl_rd_list, file_ext); 1169 heci_flush_list(&dev->ctrl_wr_list, file_ext); 1170 mutex_exit(&dev->device_lock); 1171 free: 1172 heci_free_cb_private(priv_cb); 1173 return (rets); 1174 } 1175 1176 /* 1177 * heci_remove_client_from_file_list - 1178 * remove file private data from device file list 1179 * 1180 * @dev: Device object for our driver 1181 * @host_client_id: host client id to be removed 1182 * 1183 */ 1184 void 1185 heci_remove_client_from_file_list(struct iamt_heci_device *dev, 1186 uint8_t host_client_id) 1187 { 1188 struct heci_file_private *file_pos = NULL; 1189 struct heci_file_private *file_next = NULL; 1190 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link, 1191 struct heci_file_private) { 1192 if (host_client_id == file_pos->host_client_id) { 1193 DBG("remove host client = %d, ME client = %d\n", 1194 file_pos->host_client_id, 1195 file_pos->me_client_id); 1196 list_del_init(&file_pos->link); 1197 break; 1198 } 1199 } 1200 } 1201 1202 /* 1203 * heci_fe_same_id - tell if file private data have same id 1204 * 1205 * @fe1: private data of 1. file object 1206 * @fe2: private data of 2. file object 1207 * 1208 * @return !=0 - if ids are the same, 0 - if differ. 1209 */ 1210 static inline int heci_fe_same_id(struct heci_file_private *fe1, 1211 struct heci_file_private *fe2) 1212 { 1213 return ((fe1->host_client_id == fe2->host_client_id) && 1214 (fe1->me_client_id == fe2->me_client_id)); 1215 } 1216