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 788 mutex_enter(&dev->device_lock); 789 dev->me_clients = clients; 790 mutex_exit(&dev->device_lock); 791 792 num = 0; 793 for (i = 0; i < sizeof (dev->heci_me_clients); i++) { 794 for (j = 0; j < 8; j++) { 795 if ((dev->heci_me_clients[i] & (1 << j)) != 0) { 796 client = &dev->me_clients[num]; 797 client->client_id = (i * 8) + j; 798 client->flow_ctrl_creds = 0; 799 err = host_client_properties(dev, client); 800 if (err != 0) { 801 mutex_enter(&dev->device_lock); 802 kmem_free(dev->me_clients, 803 dev->num_heci_me_clients* 804 sizeof (struct heci_me_client)); 805 dev->me_clients = NULL; 806 mutex_exit(&dev->device_lock); 807 return (err); 808 } 809 num++; 810 } 811 } 812 } 813 814 return (0); 815 } 816 817 /* 818 * heci_init_file_private - initializes private file structure. 819 * 820 * @priv: private file structure to be initialized 821 * @file: the file structure 822 * 823 */ 824 static void 825 heci_init_file_private(struct heci_file_private *priv, 826 struct heci_file *file) 827 { 828 _NOTE(ARGUNUSED(file)); 829 830 (void) memset(priv, 0, sizeof (struct heci_file_private)); 831 mutex_init(&priv->file_lock, NULL, MUTEX_DRIVER, NULL); 832 mutex_init(&priv->read_io_lock, NULL, MUTEX_DRIVER, NULL); 833 mutex_init(&priv->write_io_lock, NULL, MUTEX_DRIVER, NULL); 834 cv_init(&priv->rx_wait, NULL, CV_DRIVER, NULL); 835 DBG("priv->rx_wait =%p\n", (void *)&priv->rx_wait); 836 LIST_INIT_HEAD(&priv->link); 837 priv->reading_state = HECI_IDLE; 838 priv->writing_state = HECI_IDLE; 839 } 840 841 /* 842 * heci_find_me_client - search for ME client guid 843 * sets client_id in heci_file_private if found 844 * @dev: Device object for our driver 845 * @priv: private file structure to set client_id in 846 * @cguid: searched guid of ME client 847 * @client_id: id of host client to be set in file private structure 848 * 849 * @return ME client index 850 */ 851 static uint8_t 852 heci_find_me_client(struct iamt_heci_device *dev, 853 struct heci_file_private *priv, 854 const struct guid *cguid, uint8_t client_id) 855 { 856 uint8_t i; 857 858 if ((dev == NULL) || (priv == NULL) || (cguid == NULL)) 859 return (0); 860 861 for (i = 0; i < dev->num_heci_me_clients; i++) { 862 if (memcmp(cguid, 863 &dev->me_clients[i].props.protocol_name, 864 sizeof (struct guid)) == 0) { 865 priv->me_client_id = dev->me_clients[i].client_id; 866 priv->state = HECI_FILE_CONNECTING; 867 priv->host_client_id = client_id; 868 869 list_add_tail(&priv->link, &dev->file_list); 870 return (i); 871 } 872 } 873 return (0); 874 } 875 876 /* 877 * heci_check_asf_mode - check for ASF client 878 * 879 * @dev: Device object for our driver 880 * 881 */ 882 static void 883 heci_check_asf_mode(struct iamt_heci_device *dev) 884 { 885 uint8_t i; 886 887 mutex_enter(&dev->device_lock); 888 dev->asf_mode = 0; 889 /* find ME ASF client - otherwise assume AMT mode */ 890 DBG("find ME ASF client - otherwise assume AMT mode.\n"); 891 for (i = 0; i < dev->num_heci_me_clients; i++) { 892 if (memcmp(&heci_asf_guid, 893 &dev->me_clients[i].props.protocol_name, 894 sizeof (struct guid)) == 0) { 895 dev->asf_mode = 1; 896 mutex_exit(&dev->device_lock); 897 DBG("found ME ASF client.\n"); 898 return; 899 } 900 } 901 mutex_exit(&dev->device_lock); 902 DBG("assume AMT mode.\n"); 903 } 904 905 /* 906 * heci_connect_me_client - connect ME client 907 * @dev: Device object for our driver 908 * @priv: private file structure 909 * @timeout: connect timeout in seconds 910 * 911 * @return 1 - if connected, 0 - if not 912 */ 913 static uint8_t 914 heci_connect_me_client(struct iamt_heci_device *dev, 915 struct heci_file_private *priv, 916 long timeout) 917 { 918 int err = 0; 919 clock_t delta = (clock_t)(timeout * HZ); 920 921 if ((dev == NULL) || (priv == NULL)) 922 return (0); 923 924 if (!heci_connect(dev, priv)) { 925 DBG("failed to call heci_connect for client_id=%d.\n", 926 priv->host_client_id); 927 heci_remove_client_from_file_list(dev, priv->host_client_id); 928 priv->state = HECI_FILE_DISCONNECTED; 929 return (0); 930 } 931 err = 0; 932 while (!(HECI_FILE_CONNECTED == priv->state || 933 HECI_FILE_DISCONNECTED == priv->state) && 934 err != -1) { 935 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock, 936 delta, TR_CLOCK_TICK); 937 } 938 if (HECI_FILE_CONNECTED != priv->state) { 939 heci_remove_client_from_file_list(dev, priv->host_client_id); 940 DBG("failed to connect client_id=%d state=%d.\n", 941 priv->host_client_id, priv->state); 942 if (err) 943 DBG("failed connect err=%08x\n", err); 944 priv->state = HECI_FILE_DISCONNECTED; 945 return (0); 946 } 947 DBG("successfully connected client_id=%d.\n", 948 priv->host_client_id); 949 return (1); 950 } 951 952 /* 953 * host_init_wd - heci initialization wd. 954 * 955 * @dev: Device object for our driver 956 * 957 */ 958 static void host_init_wd(struct iamt_heci_device *dev) 959 { 960 961 mutex_enter(&dev->device_lock); 962 963 heci_init_file_private(&dev->wd_file_ext, NULL); 964 965 /* look for WD client and connect to it */ 966 dev->wd_file_ext.state = HECI_FILE_DISCONNECTED; 967 dev->wd_timeout = 0; 968 969 if (dev->asf_mode) { 970 (void) memcpy(dev->wd_data, stop_wd_params, 971 HECI_WD_PARAMS_SIZE); 972 } else { 973 /* AMT mode */ 974 dev->wd_timeout = AMT_WD_VALUE; 975 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout); 976 (void) memcpy(dev->wd_data, start_wd_params, 977 HECI_WD_PARAMS_SIZE); 978 (void) memcpy(dev->wd_data + HECI_WD_PARAMS_SIZE, 979 &dev->wd_timeout, sizeof (uint16_t)); 980 } 981 982 /* find ME WD client */ 983 (void) heci_find_me_client(dev, &dev->wd_file_ext, 984 &heci_wd_guid, HECI_WD_HOST_CLIENT_ID); 985 986 DBG("check wd_file_ext\n"); 987 if (HECI_FILE_CONNECTING == dev->wd_file_ext.state) { 988 if (heci_connect_me_client(dev, &dev->wd_file_ext, 15) == 1) { 989 DBG("dev->wd_timeout=%d.\n", dev->wd_timeout); 990 if (dev->wd_timeout != 0) 991 dev->wd_due_counter = 1; 992 else 993 dev->wd_due_counter = 0; 994 DBG("successfully connected to WD client.\n"); 995 } 996 } else 997 DBG("failed to find WD client.\n"); 998 999 1000 mutex_exit(&dev->device_lock); 1001 } 1002 1003 1004 /* 1005 * host_init_iamthif - heci initialization iamthif client. 1006 * 1007 * @dev: Device object for our driver 1008 * 1009 */ 1010 static void 1011 host_init_iamthif(struct iamt_heci_device *dev) 1012 { 1013 uint8_t i; 1014 1015 mutex_enter(&dev->device_lock); 1016 1017 heci_init_file_private(&dev->iamthif_file_ext, NULL); 1018 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTED; 1019 1020 /* find ME PTHI client */ 1021 i = heci_find_me_client(dev, &dev->iamthif_file_ext, 1022 &heci_pthi_guid, HECI_IAMTHIF_HOST_CLIENT_ID); 1023 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTING) { 1024 DBG("failed to find iamthif client.\n"); 1025 mutex_exit(&dev->device_lock); 1026 return; 1027 } 1028 1029 ASSERT(dev->me_clients[i].props.max_msg_length == IAMTHIF_MTU); 1030 1031 if (heci_connect_me_client(dev, &dev->iamthif_file_ext, 15) == 1) { 1032 DBG("connected to iamthif client.\n"); 1033 dev->iamthif_state = HECI_IAMTHIF_IDLE; 1034 } 1035 mutex_exit(&dev->device_lock); 1036 } 1037 1038 /* 1039 * heci_alloc_file_private - allocates a private file structure and set it up. 1040 * @file: the file structure 1041 * 1042 * @return The allocated file or NULL on failure 1043 */ 1044 struct heci_file_private * 1045 heci_alloc_file_private(struct heci_file *file) 1046 { 1047 struct heci_file_private *priv; 1048 1049 priv = kmem_zalloc(sizeof (struct heci_file_private), KM_SLEEP); 1050 1051 heci_init_file_private(priv, file); 1052 1053 return (priv); 1054 } 1055 1056 /* 1057 * heci_free_file_private - free a private file structure that were previously 1058 * allocated by heci_alloc_file_private 1059 */ 1060 void 1061 heci_free_file_private(struct heci_file_private *priv) 1062 { 1063 mutex_destroy(&priv->file_lock); 1064 mutex_destroy(&priv->read_io_lock); 1065 mutex_destroy(&priv->write_io_lock); 1066 cv_destroy(&priv->rx_wait); 1067 kmem_free(priv, sizeof (struct heci_file_private)); 1068 1069 } 1070 1071 /* 1072 * heci_disconnect_host_client - send disconnect message to fw from host 1073 * client. 1074 * 1075 * @dev: Device object for our driver 1076 * @file_ext: private data of the file object 1077 * 1078 * @return 0 on success, <0 on failure. 1079 */ 1080 int 1081 heci_disconnect_host_client(struct iamt_heci_device *dev, 1082 struct heci_file_private *file_ext) 1083 { 1084 int rets, err; 1085 long timeout = 15; /* 15 seconds */ 1086 struct heci_cb_private *priv_cb; 1087 clock_t delta = (clock_t)(timeout * HZ); 1088 1089 if ((!dev) || (!file_ext)) 1090 return (-ENODEV); 1091 1092 if (file_ext->state != HECI_FILE_DISCONNECTING) 1093 return (0); 1094 1095 priv_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP); 1096 1097 LIST_INIT_HEAD(&priv_cb->cb_list); 1098 priv_cb->file_private = file_ext; 1099 priv_cb->major_file_operations = HECI_CLOSE; 1100 mutex_enter(&dev->device_lock); 1101 if (dev->host_buffer_is_empty) { 1102 dev->host_buffer_is_empty = 0; 1103 if (heci_disconnect(dev, file_ext)) { 1104 list_add_tail(&priv_cb->cb_list, 1105 &dev->ctrl_rd_list.heci_cb.cb_list); 1106 } else { 1107 mutex_exit(&dev->device_lock); 1108 rets = -ENODEV; 1109 DBG("failed to call heci_disconnect.\n"); 1110 goto free; 1111 } 1112 } else { 1113 DBG("add disconnect cb to control write list\n"); 1114 list_add_tail(&priv_cb->cb_list, 1115 &dev->ctrl_wr_list.heci_cb.cb_list); 1116 } 1117 1118 err = 0; 1119 while (err != -1 && 1120 (HECI_FILE_DISCONNECTED != file_ext->state)) { 1121 1122 err = cv_reltimedwait(&dev->wait_recvd_msg, &dev->device_lock, 1123 delta, TR_CLOCK_TICK); 1124 } 1125 mutex_exit(&dev->device_lock); 1126 1127 if (HECI_FILE_DISCONNECTED == file_ext->state) { 1128 rets = 0; 1129 DBG("successfully disconnected from fw client." 1130 " me_client_id:%d, host_client_id:%d\n", 1131 file_ext->me_client_id, 1132 file_ext->host_client_id); 1133 } else { 1134 rets = -ENODEV; 1135 if (HECI_FILE_DISCONNECTED != file_ext->state) 1136 DBG("wrong status client disconnect.\n"); 1137 1138 if (err) 1139 DBG("wait failed disconnect err=%08x\n", err); 1140 1141 DBG("failed to disconnect from fw client.\n" 1142 " me_client_id:%d, host_client_id:%d\n", 1143 file_ext->me_client_id, 1144 file_ext->host_client_id); 1145 } 1146 1147 mutex_enter(&dev->device_lock); 1148 heci_flush_list(&dev->ctrl_rd_list, file_ext); 1149 heci_flush_list(&dev->ctrl_wr_list, file_ext); 1150 mutex_exit(&dev->device_lock); 1151 free: 1152 heci_free_cb_private(priv_cb); 1153 return (rets); 1154 } 1155 1156 /* 1157 * heci_remove_client_from_file_list - 1158 * remove file private data from device file list 1159 * 1160 * @dev: Device object for our driver 1161 * @host_client_id: host client id to be removed 1162 * 1163 */ 1164 void 1165 heci_remove_client_from_file_list(struct iamt_heci_device *dev, 1166 uint8_t host_client_id) 1167 { 1168 struct heci_file_private *file_pos = NULL; 1169 struct heci_file_private *file_next = NULL; 1170 list_for_each_entry_safe(file_pos, file_next, &dev->file_list, link, 1171 struct heci_file_private) { 1172 if (host_client_id == file_pos->host_client_id) { 1173 DBG("remove host client = %d, ME client = %d\n", 1174 file_pos->host_client_id, 1175 file_pos->me_client_id); 1176 list_del_init(&file_pos->link); 1177 break; 1178 } 1179 } 1180 } 1181 1182 /* 1183 * heci_fe_same_id - tell if file private data have same id 1184 * 1185 * @fe1: private data of 1. file object 1186 * @fe2: private data of 2. file object 1187 * 1188 * @return !=0 - if ids are the same, 0 - if differ. 1189 */ 1190 static inline int heci_fe_same_id(struct heci_file_private *fe1, 1191 struct heci_file_private *fe2) 1192 { 1193 return ((fe1->host_client_id == fe2->host_client_id) && 1194 (fe1->me_client_id == fe2->me_client_id)); 1195 }