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 } 1204