1 /* 2 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * Part of Intel(R) Manageability Engine Interface Linux driver 8 * 9 * Copyright (c) 2003 - 2008 Intel Corp. 10 * All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions, and the following disclaimer, 17 * without modification. 18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 19 * substantially similar to the "NO WARRANTY" disclaimer below 20 * ("Disclaimer") and any redistribution must be conditioned upon 21 * including a substantially similar Disclaimer requirement for further 22 * binary redistribution. 23 * 3. Neither the names of the above-listed copyright holders nor the names 24 * of any contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * Alternatively, this software may be distributed under the terms of the 28 * GNU General Public License ("GPL") version 2 as published by the Free 29 * Software Foundation. 30 * 31 * NO WARRANTY 32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 42 * POSSIBILITY OF SUCH DAMAGES. 43 * 44 */ 45 46 #pragma ident "@(#)heci_main.c 1.7 08/03/07 SMI" 47 48 #include <sys/types.h> 49 #include <sys/note.h> 50 #include <sys/cmn_err.h> 51 #include <sys/conf.h> 52 #include <sys/ddi.h> 53 #include <sys/ddi_impldefs.h> 54 #include <sys/devops.h> 55 #include <sys/instance.h> 56 #include <sys/modctl.h> 57 #include <sys/open.h> 58 #include <sys/stat.h> 59 #include <sys/sunddi.h> 60 #include <sys/file.h> 61 #include <sys/priv.h> 62 #include <sys/systm.h> 63 #include <sys/mkdev.h> 64 #include <sys/list.h> 65 #include <sys/pci.h> 66 #include "heci_data_structures.h" 67 68 #include "heci.h" 69 #include "heci_interface.h" 70 71 #define MAJOR_VERSION 5 72 #define MINOR_VERSION 0 73 #define QUICK_FIX_NUMBER 0 74 #define VER_BUILD 30 75 76 #define str(s) name(s) 77 #define name(s) #s 78 #define HECI_DRIVER_VERSION str(MAJOR_VERSION) "." str(MINOR_VERSION) \ 79 "." str(QUICK_FIX_NUMBER) "." str(VER_BUILD) 80 81 #define HECI_READ_TIMEOUT 45 82 83 #define HECI_DRIVER_NAME "heci" 84 85 /* 86 * heci driver strings 87 */ 88 char heci_driver_name[] = HECI_DRIVER_NAME; 89 char heci_driver_string[] = "Intel(R) Management Engine Interface"; 90 char heci_driver_version[] = HECI_DRIVER_VERSION; 91 char heci_copyright[] = "Copyright (c) 2003 - 2008 Intel Corporation."; 92 93 void * heci_soft_state_p = NULL; 94 95 #ifdef DEBUG 96 int heci_debug = 0; 97 #endif 98 99 /* 100 * Local Function Prototypes 101 */ 102 static int heci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 103 static int heci_initialize(dev_info_t *dip, struct iamt_heci_device *device); 104 static int heci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, 105 void *arg, void **result); 106 static int heci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 107 static int heci_quiesce(dev_info_t *dip); 108 static int heci_open(dev_t *devp, int flags, int otyp, cred_t *credp); 109 static int heci_close(dev_t dev, int flag, int otyp, struct cred *cred); 110 static int heci_read(dev_t dev, struct uio *uio_p, cred_t *cred_p); 111 static int heci_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 112 cred_t *cr, int *rval); 113 static int heci_write(dev_t dev, struct uio *uio_p, struct cred *cred); 114 static int heci_poll(dev_t dev, short events, int anyyet, 115 short *reventsp, struct pollhead **phpp); 116 static struct heci_cb_private *find_read_list_entry( 117 struct iamt_heci_device *dev, 118 struct heci_file_private *file_ext); 119 static inline int heci_fe_same_id(struct heci_file_private *fe1, 120 struct heci_file_private *fe2); 121 122 static void heci_resume(dev_info_t *dip); 123 static int heci_suspend(dev_info_t *dip); 124 static uint16_t g_sus_wd_timeout; 125 126 static struct cb_ops heci_cb_ops = { 127 heci_open, /* open */ 128 heci_close, /* close */ 129 nodev, /* strategy */ 130 nodev, /* print */ 131 nodev, /* dump */ 132 heci_read, /* read */ 133 heci_write, /* write */ 134 heci_ioctl, /* ioctl */ 135 nodev, /* devmap */ 136 nodev, /* mmap */ 137 nodev, /* segmap */ 138 heci_poll, /* poll */ 139 ddi_prop_op, /* cb_prop op */ 140 NULL, /* stream tab */ 141 D_MP /* Driver Compatability Flags */ 142 }; 143 144 static struct dev_ops heci_dev_ops = { 145 DEVO_REV, /* devo_rev */ 146 0, /* refcnt */ 147 heci_getinfo, /* get_dev_info */ 148 nulldev, /* identify */ 149 nulldev, /* probe */ 150 heci_attach, /* attach */ 151 heci_detach, /* detach */ 152 nodev, /* reset */ 153 &heci_cb_ops, /* Driver Ops */ 154 (struct bus_ops *)NULL, /* Bus Operations */ 155 NULL, /* power */ 156 heci_quiesce /* devo_quiesce */ 157 }; 158 159 /* 160 * Module linkage information for the kernel 161 */ 162 163 static struct modldrv modldrv = { 164 &mod_driverops, /* Type of Module = Driver */ 165 heci_driver_string, /* Driver Identifier string. */ 166 &heci_dev_ops, /* Driver Ops. */ 167 }; 168 169 static struct modlinkage modlinkage = { 170 MODREV_1, (void *)&modldrv, NULL 171 }; 172 173 /* 174 * Module Initialization functions. 175 */ 176 177 int 178 _init(void) 179 { 180 int stat; 181 182 /* Allocate soft state */ 183 if ((stat = ddi_soft_state_init(&heci_soft_state_p, 184 sizeof (struct iamt_heci_device), 1)) != DDI_SUCCESS) { 185 return (stat); 186 } 187 188 if ((stat = mod_install(&modlinkage)) != 0) 189 ddi_soft_state_fini(&heci_soft_state_p); 190 191 return (stat); 192 } 193 194 int 195 _info(struct modinfo *infop) 196 { 197 198 return (mod_info(&modlinkage, infop)); 199 } 200 201 int 202 _fini(void) 203 { 204 int stat; 205 206 if ((stat = mod_remove(&modlinkage)) != 0) 207 return (stat); 208 209 ddi_soft_state_fini(&heci_soft_state_p); 210 211 return (stat); 212 } 213 214 /* 215 * heci_attach - Driver Attach Routine 216 */ 217 static int 218 heci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 219 { 220 int instance, status; 221 struct iamt_heci_device *device; 222 223 switch (cmd) { 224 case DDI_ATTACH: 225 break; 226 case DDI_RESUME: 227 heci_resume(dip); 228 return (DDI_SUCCESS); 229 default: 230 return (DDI_FAILURE); 231 } 232 233 DBG("%s - version %s\n", heci_driver_string, heci_driver_version); 234 DBG("%s\n", heci_copyright); 235 236 instance = ddi_get_instance(dip); /* find out which unit */ 237 status = ddi_soft_state_zalloc(heci_soft_state_p, instance); 238 if (status != DDI_SUCCESS) 239 return (DDI_FAILURE); 240 device = ddi_get_soft_state(heci_soft_state_p, instance); 241 ASSERT(device != NULL); /* can't fail - we only just allocated it */ 242 243 device->dip = dip; 244 245 status = heci_initialize(dip, device); 246 if (status != DDI_SUCCESS) { 247 ddi_soft_state_free(heci_soft_state_p, instance); 248 return (DDI_FAILURE); 249 } 250 251 status = ddi_create_minor_node(dip, "AMT", S_IFCHR, 252 MAKE_MINOR_NUM(HECI_MINOR_NUMBER, instance), 253 DDI_PSEUDO, 0); 254 255 if (status != DDI_SUCCESS) { 256 257 ddi_remove_minor_node(dip, NULL); 258 ddi_soft_state_free(heci_soft_state_p, instance); 259 return (DDI_FAILURE); 260 } 261 262 263 return (status); 264 } 265 266 /* 267 * heci_probe - Device Initialization Routine 268 */ 269 static int 270 heci_initialize(dev_info_t *dip, struct iamt_heci_device *device) 271 { 272 int err; 273 ddi_device_acc_attr_t attr; 274 275 err = ddi_get_iblock_cookie(dip, 0, &device->sc_iblk); 276 if (err != DDI_SUCCESS) { 277 cmn_err(CE_WARN, "heci_probe():" 278 " ddi_get_iblock_cookie() failed\n"); 279 goto end; 280 } 281 /* initializes the heci device structure */ 282 init_heci_device(dip, device); 283 284 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 285 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 286 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 287 288 if (ddi_regs_map_setup(dip, 1, (caddr_t *)&device->mem_addr, 0, 0, 289 &attr, &device->io_handle) != DDI_SUCCESS) { 290 cmn_err(CE_WARN, "heci%d: unable to map PCI regs\n", 291 ddi_get_instance(dip)); 292 goto fini_heci_device; 293 } 294 295 err = ddi_add_intr(dip, 0, &device->sc_iblk, NULL, 296 heci_isr_interrupt, (caddr_t)device); 297 if (err != DDI_SUCCESS) { 298 cmn_err(CE_WARN, "heci_probe(): ddi_add_intr() failed\n"); 299 goto unmap_memory; 300 } 301 302 if (heci_hw_init(device)) { 303 cmn_err(CE_WARN, "init hw failure.\n"); 304 err = -ENODEV; 305 goto release_irq; 306 } 307 (void) heci_initialize_clients(device); 308 if (device->heci_state != HECI_ENABLED) { 309 err = -ENODEV; 310 goto release_hw; 311 } 312 if (device->wd_timeout) 313 device->wd_timer = timeout(heci_wd_timer, device, 1); 314 315 DBG("heci driver initialization successful.\n"); 316 return (0); 317 318 release_hw: 319 /* disable interrupts */ 320 device->host_hw_state = read_heci_register(device, H_CSR); 321 heci_csr_disable_interrupts(device); 322 323 release_irq: 324 ddi_remove_intr(dip, 0, device->sc_iblk); 325 unmap_memory: 326 if (device->mem_addr) 327 ddi_regs_map_free(&device->io_handle); 328 fini_heci_device: 329 fini_heci_device(device); 330 end: 331 cmn_err(CE_WARN, "heci driver initialization failed.\n"); 332 return (err); 333 } 334 335 void 336 heci_destroy_locks(struct iamt_heci_device *device_object) 337 { 338 339 mutex_destroy(&device_object->iamthif_file_ext.file_lock); 340 mutex_destroy(&device_object->iamthif_file_ext.read_io_lock); 341 mutex_destroy(&device_object->iamthif_file_ext.write_io_lock); 342 343 mutex_destroy(&device_object->wd_file_ext.file_lock); 344 mutex_destroy(&device_object->wd_file_ext.read_io_lock); 345 mutex_destroy(&device_object->wd_file_ext.write_io_lock); 346 mutex_destroy(&device_object->device_lock); 347 348 cv_destroy(&device_object->iamthif_file_ext.rx_wait); 349 cv_destroy(&device_object->wd_file_ext.rx_wait); 350 cv_destroy(&device_object->wait_recvd_msg); 351 cv_destroy(&device_object->wait_stop_wd); 352 } 353 354 /* 355 * heci_remove - Device Removal Routine 356 * 357 * @pdev: PCI device information struct 358 * 359 * heci_remove is called by the PCI subsystem to alert the driver 360 * that it should release a PCI device. 361 */ 362 static int 363 heci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 364 { 365 struct iamt_heci_device *dev; 366 int err; 367 368 dev = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip)); 369 ASSERT(dev != NULL); 370 371 switch (cmd) { 372 case DDI_SUSPEND: 373 err = heci_suspend(dip); 374 if (err) 375 return (DDI_FAILURE); 376 else 377 return (DDI_SUCCESS); 378 379 case DDI_DETACH: 380 break; 381 382 default: 383 return (DDI_FAILURE); 384 } 385 386 if (dev->wd_timer) 387 (void) untimeout(dev->wd_timer); 388 389 mutex_enter(&dev->device_lock); 390 if (dev->wd_file_ext.state == HECI_FILE_CONNECTED && 391 dev->wd_timeout) { 392 dev->wd_timeout = 0; 393 dev->wd_due_counter = 0; 394 (void) memcpy(dev->wd_data, stop_wd_params, 395 HECI_WD_PARAMS_SIZE); 396 dev->stop = 1; 397 if (dev->host_buffer_is_empty && 398 flow_ctrl_creds(dev, &dev->wd_file_ext)) { 399 dev->host_buffer_is_empty = 0; 400 401 if (!heci_send_wd(dev)) { 402 DBG("send stop WD failed\n"); 403 } else 404 flow_ctrl_reduce(dev, &dev->wd_file_ext); 405 406 dev->wd_pending = 0; 407 } else 408 dev->wd_pending = 1; 409 410 dev->wd_stoped = 0; 411 412 err = 0; 413 while (!dev->wd_stoped && err != -1) { 414 clock_t tm; 415 tm = ddi_get_lbolt(); 416 err = cv_timedwait(&dev->wait_stop_wd, 417 &dev->device_lock, 418 tm + 10*HZ); 419 } 420 421 if (!dev->wd_stoped) { 422 DBG("stop wd failed to complete.\n"); 423 } else { 424 DBG("stop wd complete.\n"); 425 } 426 427 } 428 429 mutex_exit(&dev->device_lock); 430 431 if (dev->iamthif_file_ext.state == HECI_FILE_CONNECTED) { 432 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTING; 433 (void) heci_disconnect_host_client(dev, 434 &dev->iamthif_file_ext); 435 } 436 if (dev->wd_file_ext.state == HECI_FILE_CONNECTED) { 437 dev->wd_file_ext.state = HECI_FILE_DISCONNECTING; 438 (void) heci_disconnect_host_client(dev, 439 &dev->wd_file_ext); 440 } 441 442 443 /* remove entry if already in list */ 444 DBG("list del iamthif and wd file list.\n"); 445 heci_remove_client_from_file_list(dev, dev->wd_file_ext. 446 host_client_id); 447 heci_remove_client_from_file_list(dev, 448 dev->iamthif_file_ext.host_client_id); 449 450 dev->iamthif_current_cb = NULL; 451 dev->iamthif_file_ext.file = NULL; 452 dev->num_heci_me_clients = 0; 453 454 455 /* disable interrupts */ 456 heci_csr_disable_interrupts(dev); 457 458 ddi_remove_intr(dip, 0, dev->sc_iblk); 459 460 if (dev->work) 461 ddi_taskq_destroy(dev->work); 462 if (dev->reinit_tsk) 463 ddi_taskq_destroy(dev->reinit_tsk); 464 if (dev->mem_addr) 465 ddi_regs_map_free(&dev->io_handle); 466 467 if (dev->me_clients && dev->num_heci_me_clients > 0) { 468 kmem_free(dev->me_clients, sizeof (struct heci_me_client) * 469 dev->num_heci_me_clients); 470 } 471 472 heci_destroy_locks(dev); 473 474 ddi_remove_minor_node(dip, NULL); 475 ddi_soft_state_free(heci_soft_state_p, ddi_get_instance(dip)); 476 477 return (DDI_SUCCESS); 478 } 479 480 481 static int 482 heci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 483 { 484 int error = DDI_SUCCESS; 485 struct iamt_heci_device *device; 486 int minor, instance; 487 488 _NOTE(ARGUNUSED(dip)) 489 490 switch (cmd) { 491 case DDI_INFO_DEVT2DEVINFO: 492 minor = getminor((dev_t)arg); 493 instance = HECI_MINOR_TO_INSTANCE(minor); 494 if (!(device = ddi_get_soft_state(heci_soft_state_p, instance))) 495 *result = NULL; 496 else 497 *result = device->dip; 498 break; 499 case DDI_INFO_DEVT2INSTANCE: 500 minor = getminor((dev_t)arg); 501 instance = HECI_MINOR_TO_INSTANCE(minor); 502 *result = (void *)((long)minor); 503 break; 504 default: 505 error = DDI_FAILURE; 506 break; 507 } 508 return (error); 509 } 510 /* 511 * heci_clear_list - remove all callbacks associated with file 512 * from heci_cb_list 513 * 514 * @file: file information struct 515 * @heci_cb_list: callbacks list 516 * 517 * heci_clear_list is called to clear resources associated with file 518 * when application calls close function or Ctrl-C was pressed 519 * 520 * @return 1 if callback removed from the list, 0 otherwise 521 */ 522 static int 523 heci_clear_list(struct iamt_heci_device *dev, 524 struct heci_file *file, struct list_node *heci_cb_list) 525 { 526 struct heci_cb_private *priv_cb_pos = NULL; 527 struct heci_cb_private *priv_cb_next = NULL; 528 struct heci_file *file_temp; 529 int rets = 0; 530 531 /* list all list member */ 532 list_for_each_entry_safe(priv_cb_pos, priv_cb_next, 533 heci_cb_list, cb_list, struct heci_cb_private) { 534 file_temp = (struct heci_file *)priv_cb_pos->file_object; 535 /* check if list member associated with a file */ 536 if (file_temp == file) { 537 /* remove member from the list */ 538 list_del(&priv_cb_pos->cb_list); 539 /* check if cb equal to current iamthif cb */ 540 if (dev->iamthif_current_cb == priv_cb_pos) { 541 dev->iamthif_current_cb = NULL; 542 /* send flow control to iamthif client */ 543 if (!heci_send_flow_control(dev, 544 &dev->iamthif_file_ext)) { 545 DBG("sending flow control failed\n"); 546 } 547 } 548 /* free all allocated buffers */ 549 heci_free_cb_private(priv_cb_pos); 550 rets = 1; 551 } 552 } 553 return (rets); 554 } 555 556 /* 557 * heci_clear_lists - remove all callbacks associated with file 558 * 559 * @dev: device information struct 560 * @file: file information struct 561 * 562 * heci_clear_lists is called to clear resources associated with file 563 * when application calls close function or Ctrl-C was pressed 564 * 565 * @return 1 if callback removed from the list, 0 otherwise 566 */ 567 static int 568 heci_clear_lists(struct iamt_heci_device *dev, struct heci_file *file) 569 { 570 int rets = 0; 571 572 /* remove callbacks associated with a file */ 573 (void) heci_clear_list(dev, file, &dev->pthi_cmd_list.heci_cb.cb_list); 574 if (heci_clear_list(dev, file, 575 &dev->pthi_read_complete_list.heci_cb.cb_list)) 576 rets = 1; 577 578 (void) heci_clear_list(dev, file, &dev->ctrl_rd_list.heci_cb.cb_list); 579 580 if (heci_clear_list(dev, file, &dev->ctrl_wr_list.heci_cb.cb_list)) 581 rets = 1; 582 583 if (heci_clear_list(dev, file, 584 &dev->write_waiting_list.heci_cb.cb_list)) 585 rets = 1; 586 587 if (heci_clear_list(dev, file, &dev->write_list.heci_cb.cb_list)) 588 rets = 1; 589 590 /* check if iamthif_current_cb not NULL */ 591 if (dev->iamthif_current_cb && (!rets)) { 592 /* check file and iamthif current cb association */ 593 if (dev->iamthif_current_cb->file_object == file) { 594 /* remove cb */ 595 heci_free_cb_private(dev->iamthif_current_cb); 596 dev->iamthif_current_cb = NULL; 597 rets = 1; 598 } 599 } 600 return (rets); 601 } 602 603 /* 604 * heci_open - the open function 605 */ 606 static int 607 heci_open(dev_t *devp, int flags, int otyp, cred_t *credp) 608 { 609 struct iamt_heci_device *dev; 610 struct heci_file_private *file_ext = NULL; 611 int minor, if_num, instance; 612 struct heci_file *file; 613 614 _NOTE(ARGUNUSED(flags, credp)) 615 616 minor = getminor(*devp); 617 618 DBG("heci_open: enter...\n"); 619 620 /* 621 * Make sure the open is for the right file type. 622 */ 623 if (otyp != OTYP_CHR) 624 return (EINVAL); 625 626 instance = HECI_MINOR_TO_INSTANCE(minor); 627 if_num = HECI_MINOR_TO_IFNUM(minor); 628 629 dev = ddi_get_soft_state(heci_soft_state_p, instance); 630 631 if ((if_num < HECI_MINOR_NUMBER) || (!dev)) 632 return (-ENODEV); 633 634 file_ext = heci_alloc_file_private(NULL); 635 if (file_ext == NULL) 636 return (-ENOMEM); 637 638 mutex_enter(&dev->device_lock); 639 if (dev->heci_state != HECI_ENABLED) { 640 mutex_exit(&dev->device_lock); 641 kmem_free(file_ext, sizeof (struct heci_file_private)); 642 file_ext = NULL; 643 return (-ENODEV); 644 } 645 if (dev->open_handle_count >= HECI_MAX_OPEN_HANDLE_COUNT) { 646 mutex_exit(&dev->device_lock); 647 kmem_free(file_ext, sizeof (struct heci_file_private)); 648 file_ext = NULL; 649 return (-ENFILE); 650 } 651 dev->open_handle_count++; 652 list_add_tail(&file_ext->link, &dev->file_list); 653 while ((dev->heci_host_clients[dev->current_host_client_id / 8] 654 & (1 << (dev->current_host_client_id % 8))) != 0) { 655 656 dev->current_host_client_id++; 657 dev->current_host_client_id %= HECI_MAX_OPEN_HANDLE_COUNT; 658 DBG("current_host_client_id = %d\n", 659 dev->current_host_client_id); 660 DBG("dev->open_handle_count = %lu\n", 661 dev->open_handle_count); 662 } 663 DBG("current_host_client_id = %d\n", dev->current_host_client_id); 664 file_ext->host_client_id = dev->current_host_client_id; 665 *devp = makedevice(getmajor(*devp), 666 MAKE_MINOR_NUM(dev->current_host_client_id, instance)); 667 file = &dev->files[dev->current_host_client_id]; 668 dev->heci_host_clients[file_ext->host_client_id / 8] |= 669 (1 << (file_ext->host_client_id % 8)); 670 mutex_exit(&dev->device_lock); 671 mutex_enter(&file_ext->file_lock); 672 file_ext->state = HECI_FILE_INITIALIZING; 673 file_ext->sm_state = 0; 674 675 file->private_data = file_ext; 676 mutex_exit(&file_ext->file_lock); 677 678 return (0); 679 } 680 681 /* 682 * heci_close - the close function 683 */ 684 static int 685 heci_close(dev_t devt, int flag, int otyp, struct cred *cred) 686 { 687 int rets = 0; 688 int minor, if_num, instance; 689 struct heci_file_private *file_ext; 690 struct heci_cb_private *priv_cb = NULL; 691 struct iamt_heci_device *dev; 692 struct heci_file *file; 693 694 _NOTE(ARGUNUSED(flag, otyp, cred)) 695 696 minor = getminor(devt); 697 698 instance = HECI_MINOR_TO_INSTANCE(minor); 699 if_num = HECI_MINOR_TO_IFNUM(minor); 700 701 dev = ddi_get_soft_state(heci_soft_state_p, instance); 702 703 file = &dev->files[if_num]; 704 file_ext = file->private_data; 705 706 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) 707 return (-ENODEV); 708 709 if (file_ext != &dev->iamthif_file_ext) { 710 mutex_enter(&file_ext->file_lock); 711 if (file_ext->state == HECI_FILE_CONNECTED) { 712 file_ext->state = HECI_FILE_DISCONNECTING; 713 mutex_exit(&file_ext->file_lock); 714 DBG("disconnecting client host client = %d, " 715 "ME client = %d\n", 716 file_ext->host_client_id, 717 file_ext->me_client_id); 718 rets = heci_disconnect_host_client(dev, file_ext); 719 mutex_enter(&file_ext->file_lock); 720 } 721 mutex_enter(&dev->device_lock); 722 heci_flush_queues(dev, file_ext); 723 DBG("remove client host client = %d, ME client = %d\n", 724 file_ext->host_client_id, 725 file_ext->me_client_id); 726 727 if (dev->open_handle_count > 0) { 728 dev->heci_host_clients[file_ext->host_client_id / 8] &= 729 ~(1 << (file_ext->host_client_id % 8)); 730 dev->open_handle_count--; 731 } 732 heci_remove_client_from_file_list(dev, 733 file_ext->host_client_id); 734 735 /* free read cb */ 736 if (file_ext->read_cb != NULL) { 737 priv_cb = find_read_list_entry(dev, file_ext); 738 /* Remove entry from read list */ 739 if (priv_cb != NULL) 740 list_del(&priv_cb->cb_list); 741 742 priv_cb = file_ext->read_cb; 743 file_ext->read_cb = NULL; 744 } 745 746 mutex_exit(&dev->device_lock); 747 file->private_data = NULL; 748 mutex_exit(&file_ext->file_lock); 749 750 if (priv_cb != NULL) 751 heci_free_cb_private(priv_cb); 752 753 heci_free_file_private(file_ext); 754 } else { 755 mutex_enter(&dev->device_lock); 756 757 if (dev->open_handle_count > 0) 758 dev->open_handle_count--; 759 760 if (dev->iamthif_file_object == file && 761 dev->iamthif_state != HECI_IAMTHIF_IDLE) { 762 DBG("pthi canceled iamthif state %d\n", 763 dev->iamthif_state); 764 dev->iamthif_canceled = 1; 765 if (dev->iamthif_state == HECI_IAMTHIF_READ_COMPLETE) { 766 DBG("run next pthi iamthif cb\n"); 767 run_next_iamthif_cmd(dev); 768 } 769 } 770 771 if (heci_clear_lists(dev, file)) 772 dev->iamthif_state = HECI_IAMTHIF_IDLE; 773 774 mutex_exit(&dev->device_lock); 775 } 776 return (rets); 777 } 778 779 static struct heci_cb_private * 780 find_read_list_entry(struct iamt_heci_device *dev, 781 struct heci_file_private *file_ext) 782 { 783 struct heci_cb_private *priv_cb_pos = NULL; 784 struct heci_cb_private *priv_cb_next = NULL; 785 struct heci_file_private *file_ext_list_temp; 786 787 if (dev->read_list.status == 0 && 788 !list_empty(&dev->read_list.heci_cb.cb_list)) { 789 790 DBG("remove read_list CB \n"); 791 list_for_each_entry_safe(priv_cb_pos, 792 priv_cb_next, 793 &dev->read_list.heci_cb.cb_list, cb_list, 794 struct heci_cb_private) { 795 796 file_ext_list_temp = (struct heci_file_private *) 797 priv_cb_pos->file_private; 798 799 if ((file_ext_list_temp != NULL) && 800 heci_fe_same_id(file_ext, file_ext_list_temp)) 801 return (priv_cb_pos); 802 803 } 804 } 805 return (NULL); 806 } 807 808 /* 809 * heci_read - the read client message function. 810 */ 811 static int 812 heci_read(dev_t devt, struct uio *uio_p, cred_t *cred_p) 813 { 814 int i; 815 int rets = 0; 816 size_t length; 817 struct heci_file *file; 818 struct heci_file_private *file_ext; 819 struct heci_cb_private *priv_cb_pos = NULL; 820 int instance, minor, if_num, err; 821 struct heci_cb_private *priv_cb = NULL; 822 struct iamt_heci_device *dev; 823 824 _NOTE(ARGUNUSED(cred_p)) 825 826 minor = getminor(devt); 827 828 instance = HECI_MINOR_TO_INSTANCE(minor); 829 if_num = HECI_MINOR_TO_IFNUM(minor); 830 831 dev = ddi_get_soft_state(heci_soft_state_p, instance); 832 833 file = &dev->files[if_num]; 834 file_ext = file->private_data; 835 836 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) 837 return (-ENODEV); 838 839 mutex_enter(&dev->device_lock); 840 if (dev->heci_state != HECI_ENABLED) { 841 mutex_exit(&dev->device_lock); 842 return (-ENODEV); 843 } 844 mutex_exit(&dev->device_lock); 845 if (!file_ext) 846 return (-ENODEV); 847 848 mutex_enter(&file_ext->file_lock); 849 if ((file_ext->sm_state & HECI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) { 850 mutex_exit(&file_ext->file_lock); 851 /* Do not allow to read watchdog client */ 852 for (i = 0; i < dev->num_heci_me_clients; i++) { 853 if (memcmp(&heci_wd_guid, 854 &dev->me_clients[i].props.protocol_name, 855 sizeof (struct guid)) == 0) { 856 if (file_ext->me_client_id == 857 dev->me_clients[i].client_id) 858 return (-EBADF); 859 } 860 } 861 } else { 862 file_ext->sm_state &= ~HECI_WD_STATE_INDEPENDENCE_MSG_SENT; 863 mutex_exit(&file_ext->file_lock); 864 } 865 866 if (file_ext == &dev->iamthif_file_ext) { 867 rets = pthi_read(dev, if_num, file, uio_p); 868 goto out; 869 } 870 871 if (file_ext->read_cb && 872 file_ext->read_cb->information > UIO_OFFSET(uio_p)) { 873 priv_cb = file_ext->read_cb; 874 goto copy_buffer; 875 } else if (file_ext->read_cb && file_ext->read_cb->information > 0 && 876 file_ext->read_cb->information <= UIO_OFFSET(uio_p)) { 877 priv_cb = file_ext->read_cb; 878 rets = 0; 879 goto free; 880 } else if ( 881 (!file_ext->read_cb || file_ext->read_cb->information == 0) && 882 UIO_OFFSET(uio_p) > 0) { 883 /* Offset needs to be cleaned for contingous reads */ 884 UIO_OFFSET(uio_p) = 0; 885 rets = 0; 886 goto out; 887 } 888 889 mutex_enter(&file_ext->read_io_lock); 890 err = heci_start_read(dev, if_num, file_ext); 891 if (err != 0 && err != -EBUSY) { 892 DBG("heci start read failure with status = %d\n", err); 893 mutex_exit(&file_ext->read_io_lock); 894 rets = err; 895 goto out; 896 } 897 while (HECI_READ_COMPLETE != file_ext->reading_state && 898 HECI_FILE_INITIALIZING != file_ext->state && 899 HECI_FILE_DISCONNECTED != file_ext->state && 900 HECI_FILE_DISCONNECTING != file_ext->state) { 901 mutex_exit(&file_ext->read_io_lock); 902 mutex_enter(&dev->device_lock); 903 if (cv_wait_sig(&file_ext->rx_wait, &dev->device_lock) == 0) { 904 mutex_exit(&dev->device_lock); 905 priv_cb = file_ext->read_cb; 906 rets = -EINTR; 907 goto free; 908 } 909 mutex_exit(&dev->device_lock); 910 911 912 if (HECI_FILE_INITIALIZING == file_ext->state || 913 HECI_FILE_DISCONNECTED == file_ext->state || 914 HECI_FILE_DISCONNECTING == file_ext->state) { 915 rets = -EBUSY; 916 goto out; 917 } 918 mutex_enter(&file_ext->read_io_lock); 919 } 920 921 priv_cb = file_ext->read_cb; 922 923 if (!priv_cb) { 924 mutex_exit(&file_ext->read_io_lock); 925 return (-ENODEV); 926 } 927 if (file_ext->reading_state != HECI_READ_COMPLETE) { 928 mutex_exit(&file_ext->read_io_lock); 929 return (0); 930 } 931 mutex_exit(&file_ext->read_io_lock); 932 /* now copy the data to user space */ 933 copy_buffer: 934 DBG("priv_cb->response_buffer size - %d\n", 935 priv_cb->response_buffer.size); 936 DBG("priv_cb->information - %lu\n", priv_cb->information); 937 if (uio_p->uio_resid == 0 || uio_p->uio_resid < priv_cb->information) { 938 rets = -EMSGSIZE; 939 goto free; 940 } 941 length = (uio_p->uio_resid < 942 (priv_cb->information - uio_p->uio_offset) ? 943 uio_p->uio_resid : (priv_cb->information - uio_p->uio_offset)); 944 945 if (uiomove(priv_cb->response_buffer.data, 946 length, UIO_READ, uio_p)) { 947 rets = -EFAULT; 948 goto free; 949 } 950 else 951 rets = 0; 952 953 free: 954 mutex_enter(&dev->device_lock); 955 priv_cb_pos = find_read_list_entry(dev, file_ext); 956 /* Remove entry from read list */ 957 if (priv_cb_pos != NULL) 958 list_del(&priv_cb_pos->cb_list); 959 mutex_exit(&dev->device_lock); 960 heci_free_cb_private(priv_cb); 961 mutex_enter(&file_ext->read_io_lock); 962 file_ext->reading_state = HECI_IDLE; 963 file_ext->read_cb = NULL; 964 file_ext->read_pending = 0; 965 mutex_exit(&file_ext->read_io_lock); 966 out: DBG("end heci read rets= %d\n", rets); 967 return (rets); 968 } 969 970 /* 971 * heci_write - the write function. 972 */ 973 static int 974 heci_write(dev_t devt, struct uio *uio_p, struct cred *cred) 975 { 976 int rets = 0; 977 uint8_t i; 978 size_t length; 979 struct heci_file_private *file_ext; 980 struct heci_cb_private *priv_write_cb = NULL; 981 struct heci_msg_hdr heci_hdr; 982 struct iamt_heci_device *dev; 983 unsigned long currtime = ddi_get_time(); 984 int instance, minor, if_num, err; 985 struct heci_file *file; 986 987 _NOTE(ARGUNUSED(cred)) 988 DBG("heci_write enter...\n"); 989 990 minor = getminor(devt); 991 instance = HECI_MINOR_TO_INSTANCE(minor); 992 if_num = HECI_MINOR_TO_IFNUM(minor); 993 994 dev = ddi_get_soft_state(heci_soft_state_p, instance); 995 996 file = &dev->files[if_num]; 997 file_ext = file->private_data; 998 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) 999 return (-ENODEV); 1000 1001 mutex_enter(&dev->device_lock); 1002 1003 if (dev->heci_state != HECI_ENABLED) { 1004 mutex_exit(&dev->device_lock); 1005 return (-ENODEV); 1006 } 1007 if (file_ext == &dev->iamthif_file_ext) { 1008 priv_write_cb = find_pthi_read_list_entry(dev, file); 1009 if ((priv_write_cb != NULL) && 1010 (((currtime - priv_write_cb->read_time) > 1011 IAMTHIF_READ_TIMER) || 1012 (file_ext->reading_state == HECI_READ_COMPLETE))) { 1013 UIO_OFFSET(uio_p) = 0; 1014 list_del(&priv_write_cb->cb_list); 1015 heci_free_cb_private(priv_write_cb); 1016 priv_write_cb = NULL; 1017 } 1018 } 1019 1020 /* free entry used in read */ 1021 if (file_ext->reading_state == HECI_READ_COMPLETE) { 1022 UIO_OFFSET(uio_p) = 0; 1023 priv_write_cb = find_read_list_entry(dev, file_ext); 1024 if (priv_write_cb != NULL) { 1025 list_del(&priv_write_cb->cb_list); 1026 heci_free_cb_private(priv_write_cb); 1027 priv_write_cb = NULL; 1028 mutex_enter(&file_ext->read_io_lock); 1029 file_ext->reading_state = HECI_IDLE; 1030 file_ext->read_cb = NULL; 1031 file_ext->read_pending = 0; 1032 mutex_exit(&file_ext->read_io_lock); 1033 } 1034 } else if (file_ext->reading_state == HECI_IDLE && 1035 file_ext->read_pending == 0) 1036 UIO_OFFSET(uio_p) = 0; 1037 1038 mutex_exit(&dev->device_lock); 1039 1040 priv_write_cb = kmem_zalloc(sizeof (struct heci_cb_private), KM_SLEEP); 1041 if (!priv_write_cb) 1042 return (-ENOMEM); 1043 1044 priv_write_cb->file_object = file; 1045 priv_write_cb->file_private = file_ext; 1046 priv_write_cb->request_buffer.data = 1047 kmem_zalloc(uio_p->uio_resid, KM_SLEEP); 1048 if (!priv_write_cb->request_buffer.data) { 1049 kmem_free(priv_write_cb, sizeof (struct heci_cb_private)); 1050 return (-ENOMEM); 1051 } 1052 length = (int)uio_p->uio_resid; 1053 DBG("length =%d\n", (int)length); 1054 1055 err = uiomove(priv_write_cb->request_buffer.data, 1056 length, UIO_WRITE, uio_p); 1057 if (err) { 1058 rets = err; 1059 goto fail; 1060 } 1061 1062 #define UBUFF UIO_BUFF(uio_p) 1063 1064 mutex_enter(&file_ext->file_lock); 1065 file_ext->sm_state = 0; 1066 if ((length == 4) && 1067 ((memcmp(heci_wd_state_independence_msg[0], UBUFF, 4) == 0) || 1068 (memcmp(heci_wd_state_independence_msg[1], UBUFF, 4) == 0) || 1069 (memcmp(heci_wd_state_independence_msg[2], UBUFF, 4) == 0))) 1070 1071 file_ext->sm_state |= HECI_WD_STATE_INDEPENDENCE_MSG_SENT; 1072 1073 mutex_exit(&file_ext->file_lock); 1074 1075 LIST_INIT_HEAD(&priv_write_cb->cb_list); 1076 if (file_ext == &dev->iamthif_file_ext) { 1077 priv_write_cb->response_buffer.data = 1078 kmem_zalloc(IAMTHIF_MTU, KM_SLEEP); 1079 if (!priv_write_cb->response_buffer.data) { 1080 rets = -ENOMEM; 1081 goto fail; 1082 } 1083 mutex_enter(&dev->device_lock); 1084 if (dev->heci_state != HECI_ENABLED) { 1085 mutex_exit(&dev->device_lock); 1086 rets = -ENODEV; 1087 goto fail; 1088 } 1089 for (i = 0; i < dev->num_heci_me_clients; i++) { 1090 if (dev->me_clients[i].client_id == 1091 dev->iamthif_file_ext.me_client_id) 1092 break; 1093 } 1094 1095 ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id); 1096 if ((i == dev->num_heci_me_clients) || 1097 (dev->me_clients[i].client_id != 1098 dev->iamthif_file_ext.me_client_id)) { 1099 1100 mutex_exit(&dev->device_lock); 1101 rets = -ENODEV; 1102 goto fail; 1103 } else if ((length > 1104 dev->me_clients[i].props.max_msg_length) || 1105 (length == 0)) { 1106 mutex_exit(&dev->device_lock); 1107 rets = -EMSGSIZE; 1108 goto fail; 1109 } 1110 1111 1112 priv_write_cb->response_buffer.size = IAMTHIF_MTU; 1113 priv_write_cb->major_file_operations = HECI_IOCTL; 1114 priv_write_cb->information = 0; 1115 priv_write_cb->request_buffer.size = (uint32_t)length; 1116 if (dev->iamthif_file_ext.state != HECI_FILE_CONNECTED) { 1117 mutex_exit(&dev->device_lock); 1118 rets = -ENODEV; 1119 goto fail; 1120 } 1121 1122 if (!list_empty(&dev->pthi_cmd_list.heci_cb.cb_list) || 1123 dev->iamthif_state != HECI_IAMTHIF_IDLE) { 1124 DBG("pthi_state = %d\n", (int)dev->iamthif_state); 1125 DBG("add PTHI cb to pthi cmd waiting list\n"); 1126 list_add_tail(&priv_write_cb->cb_list, 1127 &dev->pthi_cmd_list.heci_cb.cb_list); 1128 rets = 0; /* length; */ 1129 } else { 1130 DBG("call pthi write\n"); 1131 rets = pthi_write(dev, priv_write_cb); 1132 1133 if (rets != 0) { 1134 DBG("pthi write failed with status = %d\n", 1135 rets); 1136 mutex_exit(&dev->device_lock); 1137 goto fail; 1138 } 1139 rets = 0; /* length; */ 1140 } 1141 mutex_exit(&dev->device_lock); 1142 return (rets); 1143 } 1144 1145 priv_write_cb->major_file_operations = HECI_WRITE; 1146 /* make sure information is zero before we start */ 1147 1148 priv_write_cb->information = 0; 1149 priv_write_cb->request_buffer.size = (uint32_t)length; 1150 1151 mutex_enter(&dev->device_lock); 1152 mutex_enter(&file_ext->write_io_lock); 1153 DBG("host client = %d, ME client = %d\n", 1154 file_ext->host_client_id, file_ext->me_client_id); 1155 if (file_ext->state != HECI_FILE_CONNECTED) { 1156 rets = -ENODEV; 1157 DBG("host client = %d, is not connected to ME client = %d", 1158 file_ext->host_client_id, 1159 file_ext->me_client_id); 1160 1161 goto unlock; 1162 } 1163 for (i = 0; i < dev->num_heci_me_clients; i++) { 1164 if (dev->me_clients[i].client_id == 1165 file_ext->me_client_id) 1166 break; 1167 } 1168 ASSERT(dev->me_clients[i].client_id == file_ext->me_client_id); 1169 if (i == dev->num_heci_me_clients) { 1170 rets = -ENODEV; 1171 goto unlock; 1172 } 1173 if (length > dev->me_clients[i].props.max_msg_length || length == 0) { 1174 rets = -EINVAL; 1175 goto unlock; 1176 } 1177 priv_write_cb->file_private = file_ext; 1178 1179 if (flow_ctrl_creds(dev, file_ext) && 1180 dev->host_buffer_is_empty) { 1181 dev->host_buffer_is_empty = 0; 1182 if (length > ((((dev->host_hw_state & H_CBD) >> 24) * 1183 sizeof (uint32_t)) - sizeof (struct heci_msg_hdr))) { 1184 1185 heci_hdr.length = 1186 (((dev->host_hw_state & H_CBD) >> 24) * 1187 sizeof (uint32_t)) - 1188 sizeof (struct heci_msg_hdr); 1189 heci_hdr.msg_complete = 0; 1190 } else { 1191 heci_hdr.length = (uint32_t)length; 1192 heci_hdr.msg_complete = 1; 1193 } 1194 heci_hdr.host_addr = file_ext->host_client_id; 1195 heci_hdr.me_addr = file_ext->me_client_id; 1196 heci_hdr.reserved = 0; 1197 DBG("call heci_write_message header=%08x.\n", 1198 *((uint32_t *)(void *)&heci_hdr)); 1199 /* protect heci low level write */ 1200 if (!heci_write_message(dev, &heci_hdr, 1201 (unsigned char *)(priv_write_cb->request_buffer.data), 1202 heci_hdr.length)) { 1203 1204 mutex_exit(&file_ext->write_io_lock); 1205 mutex_exit(&dev->device_lock); 1206 heci_free_cb_private(priv_write_cb); 1207 rets = -ENODEV; 1208 priv_write_cb->information = 0; 1209 return (rets); 1210 } 1211 file_ext->writing_state = HECI_WRITING; 1212 priv_write_cb->information = heci_hdr.length; 1213 if (heci_hdr.msg_complete) { 1214 flow_ctrl_reduce(dev, file_ext); 1215 list_add_tail(&priv_write_cb->cb_list, 1216 &dev->write_waiting_list.heci_cb.cb_list); 1217 } else { 1218 list_add_tail(&priv_write_cb->cb_list, 1219 &dev->write_list.heci_cb.cb_list); 1220 } 1221 1222 } else { 1223 1224 priv_write_cb->information = 0; 1225 file_ext->writing_state = HECI_WRITING; 1226 list_add_tail(&priv_write_cb->cb_list, 1227 &dev->write_list.heci_cb.cb_list); 1228 } 1229 mutex_exit(&file_ext->write_io_lock); 1230 mutex_exit(&dev->device_lock); 1231 return (0); 1232 1233 unlock: 1234 mutex_exit(&file_ext->write_io_lock); 1235 mutex_exit(&dev->device_lock); 1236 fail: 1237 heci_free_cb_private(priv_write_cb); 1238 return (rets); 1239 1240 } 1241 1242 /* 1243 * heci_ioctl - the IOCTL function 1244 */ 1245 static int 1246 heci_ioctl(dev_t devt, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 1247 { 1248 int rets = 0; 1249 struct heci_file_private *file_ext; 1250 /* in user space */ 1251 struct heci_message_data *u_msg = (struct heci_message_data *)arg; 1252 struct heci_message_data k_msg; /* all in kernel on the stack */ 1253 struct iamt_heci_device *dev; 1254 int instance, minor, if_num; 1255 struct heci_file *file; 1256 1257 _NOTE(ARGUNUSED(cr, rval)) 1258 1259 minor = getminor(devt); 1260 1261 instance = HECI_MINOR_TO_INSTANCE(minor); 1262 if_num = HECI_MINOR_TO_IFNUM(minor); 1263 1264 dev = ddi_get_soft_state(heci_soft_state_p, instance); 1265 1266 file = &dev->files[if_num]; 1267 file_ext = file->private_data; 1268 1269 if ((if_num < HECI_MINOR_NUMBER) || (!dev) || (!file_ext)) 1270 return (-ENODEV); 1271 1272 mutex_enter(&dev->device_lock); 1273 if (dev->heci_state != HECI_ENABLED) { 1274 mutex_exit(&dev->device_lock); 1275 return (-ENODEV); 1276 } 1277 mutex_exit(&dev->device_lock); 1278 1279 /* first copy from user all data needed */ 1280 if (ddi_copyin(u_msg, &k_msg, sizeof (k_msg), mode)) { 1281 DBG("first copy from user all data needed filled\n"); 1282 return (-EFAULT); 1283 } 1284 #ifdef _LP64 1285 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 1286 uint32_t addr32 = (uint32_t)(uint64_t)k_msg.data; 1287 k_msg.data = (char *)(uint64_t)addr32; 1288 DBG("IPL32: k_msg.data=%p\n", (void *)k_msg.data); 1289 } 1290 #endif 1291 DBG("user message size is %d, cmd = 0x%x\n", k_msg.size, cmd); 1292 1293 switch (cmd) { 1294 case IOCTL_HECI_GET_VERSION: 1295 DBG(": IOCTL_HECI_GET_VERSION\n"); 1296 rets = heci_ioctl_get_version(dev, if_num, u_msg, k_msg, 1297 file_ext, mode); 1298 break; 1299 1300 case IOCTL_HECI_CONNECT_CLIENT: 1301 DBG(": IOCTL_HECI_CONNECT_CLIENT.\n"); 1302 rets = heci_ioctl_connect_client(dev, if_num, u_msg, k_msg, 1303 file, mode); 1304 break; 1305 1306 case IOCTL_HECI_WD: 1307 DBG(": IOCTL_HECI_WD.\n"); 1308 rets = heci_ioctl_wd(dev, if_num, k_msg, file_ext, mode); 1309 break; 1310 1311 case IOCTL_HECI_BYPASS_WD: 1312 DBG(": IOCTL_HECI_BYPASS_WD.\n"); 1313 rets = heci_ioctl_bypass_wd(dev, if_num, k_msg, file_ext, mode); 1314 break; 1315 1316 default: 1317 rets = -EINVAL; 1318 break; 1319 } 1320 return (rets); 1321 } 1322 1323 /* 1324 * heci_poll - the poll function 1325 */ 1326 static int 1327 heci_poll(dev_t devt, short events, int anyyet, 1328 short *reventsp, struct pollhead **phpp) 1329 { 1330 struct heci_file *file; 1331 struct heci_file_private *file_extension; 1332 struct iamt_heci_device *device = NULL; 1333 int instance, minor, if_num; 1334 1335 _NOTE(ARGUNUSED(events)) 1336 1337 minor = getminor(devt); 1338 1339 instance = HECI_MINOR_TO_INSTANCE(minor); 1340 if_num = HECI_MINOR_TO_IFNUM(minor); 1341 1342 device = ddi_get_soft_state(heci_soft_state_p, instance); 1343 1344 file = &device->files[if_num]; 1345 file_extension = file->private_data; 1346 1347 if ((if_num < HECI_MINOR_NUMBER) || (!device) || (!file_extension)) 1348 return (-ENODEV); 1349 1350 mutex_enter(&device->device_lock); 1351 if (device->heci_state != HECI_ENABLED) { 1352 mutex_exit(&device->device_lock); 1353 return (-ENXIO); 1354 1355 } 1356 1357 mutex_exit(&device->device_lock); 1358 1359 if (file_extension == &device->iamthif_file_ext) { 1360 1361 mutex_enter(&device->iamthif_file_ext.file_lock); 1362 1363 if (device->iamthif_state == HECI_IAMTHIF_READ_COMPLETE && 1364 device->iamthif_file_object == file) { 1365 *reventsp |= (POLLIN | POLLRDNORM); 1366 mutex_enter(&device->device_lock); 1367 DBG("heci_poll: run next pthi cb\n"); 1368 run_next_iamthif_cmd(device); 1369 mutex_exit(&device->device_lock); 1370 } else { 1371 DBG("heci_poll: iamthif no event\n"); 1372 *reventsp = 0; 1373 if (!anyyet) 1374 *phpp = &device->iamthif_file_ext.pollwait; 1375 } 1376 mutex_exit(&device->iamthif_file_ext.file_lock); 1377 1378 } else { 1379 mutex_enter(&file_extension->write_io_lock); 1380 if (HECI_WRITE_COMPLETE == file_extension->writing_state) { 1381 *reventsp |= (POLLIN | POLLRDNORM); 1382 DBG("heci_poll: file_extension poll event\n"); 1383 } else { 1384 DBG("heci_poll: file_extension no event\n"); 1385 *reventsp = 0; 1386 if (!anyyet) 1387 *phpp = &file_extension->tx_pollwait; 1388 } 1389 mutex_exit(&file_extension->write_io_lock); 1390 1391 1392 } 1393 1394 return (0); 1395 } 1396 1397 /* 1398 * heci_fe_same_id - tell if file private data have same id 1399 * 1400 * @fe1: private data of 1. file object 1401 * @fe2: private data of 2. file object 1402 * 1403 * @return !=0 - if ids are the same, 0 - if differ. 1404 */ 1405 static inline int heci_fe_same_id(struct heci_file_private *fe1, 1406 struct heci_file_private *fe2) 1407 { 1408 return ((fe1->host_client_id == fe2->host_client_id) && 1409 (fe1->me_client_id == fe2->me_client_id)); 1410 } 1411 1412 /* 1413 * Since the ME firmware won't reset itself during OS reboot, it's not enough 1414 * to only disable interrupts in quiesce(), here we do a full hand-shake 1415 * with the firmware. 1416 */ 1417 static int 1418 heci_quiesce(dev_info_t *dip) 1419 { 1420 struct iamt_heci_device *dev; 1421 int err; 1422 1423 dev = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip)); 1424 ASSERT(dev != NULL); 1425 1426 if (dev->wd_timer) 1427 (void) untimeout(dev->wd_timer); 1428 1429 mutex_enter(&dev->device_lock); 1430 if (dev->wd_file_ext.state == HECI_FILE_CONNECTED && 1431 dev->wd_timeout) { 1432 dev->wd_timeout = 0; 1433 dev->wd_due_counter = 0; 1434 (void) memcpy(dev->wd_data, stop_wd_params, 1435 HECI_WD_PARAMS_SIZE); 1436 dev->stop = 1; 1437 if (dev->host_buffer_is_empty && 1438 flow_ctrl_creds(dev, &dev->wd_file_ext)) { 1439 dev->host_buffer_is_empty = 0; 1440 1441 if (!heci_send_wd(dev)) { 1442 DBG("send stop WD failed\n"); 1443 } else 1444 flow_ctrl_reduce(dev, &dev->wd_file_ext); 1445 1446 dev->wd_pending = 0; 1447 } else 1448 dev->wd_pending = 1; 1449 dev->wd_stoped = 0; 1450 1451 err = 0; 1452 while (!dev->wd_stoped && err != -1) { 1453 clock_t tm; 1454 tm = ddi_get_lbolt(); 1455 err = cv_timedwait(&dev->wait_stop_wd, 1456 &dev->device_lock, 1457 tm + 10*HZ); 1458 } 1459 1460 if (!dev->wd_stoped) { 1461 DBG("stop wd failed to complete.\n"); 1462 } else { 1463 DBG("stop wd complete.\n"); 1464 } 1465 1466 } 1467 1468 mutex_exit(&dev->device_lock); 1469 1470 if (dev->iamthif_file_ext.state == HECI_FILE_CONNECTED) { 1471 dev->iamthif_file_ext.state = HECI_FILE_DISCONNECTING; 1472 (void) heci_disconnect_host_client(dev, 1473 &dev->iamthif_file_ext); 1474 } 1475 if (dev->wd_file_ext.state == HECI_FILE_CONNECTED) { 1476 dev->wd_file_ext.state = HECI_FILE_DISCONNECTING; 1477 (void) heci_disconnect_host_client(dev, 1478 &dev->wd_file_ext); 1479 } 1480 1481 1482 /* disable interrupts */ 1483 heci_csr_disable_interrupts(dev); 1484 1485 return (DDI_SUCCESS); 1486 } 1487 1488 static int 1489 heci_suspend(dev_info_t *dip) 1490 { 1491 struct iamt_heci_device *device; 1492 int err = 0; 1493 1494 device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip)); 1495 1496 if (device->reinit_tsk) 1497 ddi_taskq_wait(device->reinit_tsk); 1498 1499 /* Stop watchdog if exists */ 1500 if (device->wd_timer) 1501 (void) untimeout(device->wd_timer); 1502 1503 mutex_enter(&device->device_lock); 1504 1505 if (device->wd_file_ext.state == HECI_FILE_CONNECTED && 1506 device->wd_timeout) { 1507 g_sus_wd_timeout = device->wd_timeout; 1508 device->wd_timeout = 0; 1509 device->wd_due_counter = 0; 1510 (void) memcpy(device->wd_data, stop_wd_params, 1511 HECI_WD_PARAMS_SIZE); 1512 device->stop = 1; 1513 if (device->host_buffer_is_empty && 1514 flow_ctrl_creds(device, &device->wd_file_ext)) { 1515 device->host_buffer_is_empty = 0; 1516 if (!heci_send_wd(device)) { 1517 DBG("send stop WD failed\n"); 1518 } 1519 else 1520 flow_ctrl_reduce(device, &device->wd_file_ext); 1521 1522 device->wd_pending = 0; 1523 } else { 1524 device->wd_pending = 1; 1525 } 1526 device->wd_stoped = 0; 1527 1528 err = 0; 1529 while (!device->wd_stoped && err != -1) { 1530 clock_t tm; 1531 tm = ddi_get_lbolt(); 1532 err = cv_timedwait(&device->wait_stop_wd, 1533 &device->device_lock, 1534 tm + 10*HZ); 1535 } 1536 1537 if (!device->wd_stoped) { 1538 DBG("stop wd failed to complete.\n"); 1539 } else { 1540 DBG("stop wd complete %d.\n", err); 1541 err = 0; 1542 } 1543 } 1544 /* Set new heci state */ 1545 if (device->heci_state == HECI_ENABLED || 1546 device->heci_state == HECI_RECOVERING_FROM_RESET) { 1547 device->heci_state = HECI_POWER_DOWN; 1548 heci_reset(device, 0); 1549 } 1550 1551 /* Here interrupts are already disabled by heci_reset() */ 1552 1553 mutex_exit(&device->device_lock); 1554 1555 1556 return (err); 1557 } 1558 1559 static void 1560 heci_resume(dev_info_t *dip) 1561 { 1562 struct iamt_heci_device *device; 1563 1564 device = ddi_get_soft_state(heci_soft_state_p, ddi_get_instance(dip)); 1565 1566 mutex_enter(&device->device_lock); 1567 device->heci_state = HECI_POWER_UP; 1568 heci_reset(device, 1); 1569 mutex_exit(&device->device_lock); 1570 1571 /* Start watchdog if stopped in suspend */ 1572 if (g_sus_wd_timeout != 0) { 1573 device->wd_timeout = g_sus_wd_timeout; 1574 1575 (void) memcpy(device->wd_data, start_wd_params, 1576 HECI_WD_PARAMS_SIZE); 1577 (void) memcpy(device->wd_data + HECI_WD_PARAMS_SIZE, 1578 &device->wd_timeout, sizeof (uint16_t)); 1579 device->wd_due_counter = 1; 1580 1581 if (device->wd_timeout) 1582 device->wd_timer = timeout(heci_wd_timer, device, 1); 1583 1584 g_sus_wd_timeout = 0; 1585 } 1586 } 1587