1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 28 #include <sys/types.h> 29 #include <sys/stat.h> 30 #include <sys/conf.h> 31 #include <sys/modctl.h> 32 #include <sys/callb.h> 33 #include <sys/strlog.h> 34 #include <sys/cyclic.h> 35 #include <sys/rmc_comm_dp.h> 36 #include <sys/rmc_comm_dp_boot.h> 37 #include <sys/rmc_comm_drvintf.h> 38 #include <sys/rmc_comm.h> 39 #include <sys/machsystm.h> 40 #include <sys/sysevent.h> 41 #include <sys/sysevent/dr.h> 42 #include <sys/sysevent/env.h> 43 #include <sys/sysevent/eventdefs.h> 44 #include <sys/file.h> 45 #include <sys/disp.h> 46 #include <sys/reboot.h> 47 #include <sys/envmon.h> 48 #include <sys/rmclomv_impl.h> 49 #include <sys/cpu_sgnblk_defs.h> 50 #include <sys/utsname.h> 51 #include <sys/systeminfo.h> 52 #include <sys/ddi.h> 53 #include <sys/time.h> 54 #include <sys/promif.h> 55 #include <sys/sysmacros.h> 56 57 #define RMCRESBUFLEN 1024 58 #define DATE_TIME_MSG_SIZE 78 59 #define RMCLOMV_WATCHDOG_MODE "rmclomv-watchdog-mode" 60 #define DELAY_TIME 5000000 /* 5 seconds, in microseconds */ 61 #define CPU_SIGNATURE_DELAY_TIME 5000000 /* 5 secs, in microsecs */ 62 63 extern void pmugpio_watchdog_pat(); 64 65 extern int watchdog_activated; 66 static int last_watchdog_msg = 1; 67 extern int watchdog_enable; 68 extern int boothowto; 69 70 int rmclomv_watchdog_mode; 71 72 /* 73 * functions local to this driver. 74 */ 75 static int rmclomv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 76 void **resultp); 77 static int rmclomv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 78 static int rmclomv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 79 static uint_t rmclomv_break_intr(caddr_t arg); 80 static int rmclomv_add_intr_handlers(void); 81 static int rmclomv_remove_intr_handlers(void); 82 static uint_t rmclomv_event_data_handler(char *); 83 static void rmclomv_dr_data_handler(const char *, int); 84 static int rmclomv_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p); 85 static int rmclomv_close(dev_t dev, int flag, int otyp, cred_t *cred_p); 86 static int rmclomv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 87 cred_t *cred_p, int *rval_p); 88 static void rmclomv_checkrmc_start(void); 89 static void rmclomv_checkrmc_destroy(void); 90 static void rmclomv_checkrmc_wakeup(void *); 91 static void rmclomv_refresh_start(void); 92 static void rmclomv_refresh_destroy(void); 93 static void rmclomv_refresh_wakeup(void); 94 static void rmclomv_reset_cache(rmclomv_cache_section_t *new_chain, 95 rmclomv_cache_section_t *new_subchain, dp_get_sysinfo_r_t *sysinfo); 96 static rmclomv_cache_section_t *rmclomv_find_section( 97 rmclomv_cache_section_t *start, uint16_t sensor); 98 static rmclomv_cache_section_t *create_cache_section(int sensor_type, int num); 99 static int get_sensor_by_name(const rmclomv_cache_section_t *section, 100 const char *name, int *index); 101 static int validate_section_entry(rmclomv_cache_section_t *section, 102 int index); 103 static int add_names_to_section(rmclomv_cache_section_t *section); 104 static void free_section(rmclomv_cache_section_t *section); 105 static void add_section(rmclomv_cache_section_t **head, 106 rmclomv_cache_section_t *section); 107 static int rmclomv_do_cmd(int req_cmd, int resp_cmd, int resp_len, 108 intptr_t arg_req, intptr_t arg_res); 109 static void refresh_name_cache(int force_fail); 110 static void set_val_unav(envmon_sensor_t *sensor); 111 static void set_fan_unav(envmon_fan_t *fan); 112 static int do_psu_cmd(intptr_t arg, int mode, envmon_indicator_t *env_ind, 113 dp_get_psu_status_t *rmc_psu, dp_get_psu_status_r_t *rmc_psu_r, 114 int detector_type); 115 static uint_t rmc_set_watchdog_timer(uint_t timeoutval); 116 static uint_t rmc_clear_watchdog_timer(void); 117 static void send_watchdog_msg(int msg); 118 static void plat_timesync(void *arg); 119 120 static kmutex_t timesync_lock; 121 static clock_t timesync_interval = 0; 122 static timeout_id_t timesync_tid = 0; 123 124 /* 125 * Driver entry points 126 */ 127 static struct cb_ops rmclomv_cb_ops = { 128 rmclomv_open, /* open */ 129 rmclomv_close, /* close */ 130 nodev, /* strategy() */ 131 nodev, /* print() */ 132 nodev, /* dump() */ 133 nodev, /* read() */ 134 nodev, /* write() */ 135 rmclomv_ioctl, /* ioctl() */ 136 nodev, /* devmap() */ 137 nodev, /* mmap() */ 138 ddi_segmap, /* segmap() */ 139 nochpoll, /* poll() */ 140 ddi_prop_op, /* prop_op() */ 141 NULL, /* cb_str */ 142 D_NEW | D_MP /* cb_flag */ 143 }; 144 145 146 static struct dev_ops rmclomv_ops = { 147 DEVO_REV, 148 0, /* ref count */ 149 rmclomv_getinfo, /* getinfo() */ 150 nulldev, /* identify() */ 151 nulldev, /* probe() */ 152 rmclomv_attach, /* attach() */ 153 rmclomv_detach, /* detach */ 154 nodev, /* reset */ 155 &rmclomv_cb_ops, /* pointer to cb_ops structure */ 156 (struct bus_ops *)NULL, 157 nulldev, /* power() */ 158 ddi_quiesce_not_supported, /* devo_quiesce */ 159 }; 160 161 /* 162 * Loadable module support. 163 */ 164 extern struct mod_ops mod_driverops; 165 166 static struct modldrv modldrv = { 167 &mod_driverops, /* Type of module. This is a driver */ 168 "rmclomv control driver", /* Name of the module */ 169 &rmclomv_ops /* pointer to the dev_ops structure */ 170 }; 171 172 static struct modlinkage modlinkage = { 173 MODREV_1, 174 &modldrv, 175 NULL 176 }; 177 178 /* 179 * Device info 180 */ 181 static dev_info_t *rmclomv_dip = NULL; 182 static int rmclomv_break_requested = B_FALSE; 183 static ddi_softintr_t rmclomv_softintr_id; 184 static ddi_iblock_cookie_t rmclomv_soft_iblock_cookie; 185 186 extern void (*abort_seq_handler)(); 187 /* key_position is effective key-position. Set to locked if unknown */ 188 static rsci8 key_position = RMC_KEYSWITCH_POS_LOCKED; 189 /* real_key_position starts off as unknown and records value actually seen */ 190 static rsci8 real_key_position = RMC_KEYSWITCH_POS_UNKNOWN; 191 static void rmclomv_abort_seq_handler(char *msg); 192 193 /* 194 * mutexes which protect the interrupt handlers. 195 */ 196 static kmutex_t rmclomv_event_hdlr_lock; 197 static kmutex_t rmclomv_refresh_lock; 198 static kcondvar_t rmclomv_refresh_sig_cv; 199 static kmutex_t rmclomv_checkrmc_lock; 200 static kcondvar_t rmclomv_checkrmc_sig_cv; 201 202 /* 203 * mutex to protect the handle_name cache 204 */ 205 static kmutex_t rmclomv_cache_lock; 206 207 /* 208 * mutex to protect the RMC state 209 */ 210 static kmutex_t rmclomv_state_lock; 211 212 /* 213 * Payloads of the event handlers. 214 */ 215 static dp_event_notification_t rmclomv_event_payload; 216 static rmc_comm_msg_t rmclomv_event_payload_msg; 217 218 /* 219 * Checkrmc commands.. 220 */ 221 #define RMCLOMV_CHECKRMC_EXITNOW (-1) 222 #define RMCLOMV_CHECKRMC_WAIT 0 223 #define RMCLOMV_CHECKRMC_PROCESSNOW 1 224 225 /* 226 * Checkrmc thread state 227 */ 228 static int rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT; 229 static kt_did_t rmclomv_checkrmc_tid = 0; 230 231 /* 232 * RMC state data 233 */ 234 #define RMCLOMV_RMCSTATE_UNKNOWN 0 235 #define RMCLOMV_RMCSTATE_OK 1 236 #define RMCLOMV_RMCSTATE_FAILED 2 237 #define RMCLOMV_RMCSTATE_DOWNLOAD 3 238 239 /* 240 * RMC error indicator values (status from last RMC command) 241 */ 242 #define RMCLOMV_RMCERROR_NONE 0 243 244 /* fail RMC after 5 minutes without a good response */ 245 #define RMCLOMV_RMCFAILTHRESHOLD 5 246 247 /* 248 * rmclomv_rmc_state is the state reported in OperationalStatus. 249 * rmclomv_rmc_error reflects the result of the last RMC interaction. 250 * rmclomv_rmcfailcount is used by the rmclomv_checkrmc thread to count 251 * failures in its regular status polls. Once RMCLOMV_RMCFAILTHRESHOLD 252 * is reached, rmclomv_rmc_state is marked as RMCLOMV_RMCSTATE_FAILED. 253 */ 254 static int rmclomv_rmc_state = RMCLOMV_RMCSTATE_UNKNOWN; 255 static int rmclomv_rmc_error = RMCLOMV_RMCERROR_NONE; 256 static int rmclomv_rmcfailcount; 257 258 /* 259 * Refresh commands.. 260 */ 261 #define RMCLOMV_REFRESH_EXITNOW (-1) 262 #define RMCLOMV_REFRESH_WAIT 0 263 #define RMCLOMV_REFRESH_PROCESSNOW 1 264 265 /* 266 * Refresh thread state 267 */ 268 static int rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT; 269 static kt_did_t rmclomv_refresh_tid = 0; 270 271 /* 272 * timeout id 273 */ 274 static timeout_id_t timer_id; 275 276 /* 277 * Handle-name cache 278 */ 279 #define LOCK_CACHE mutex_enter(&rmclomv_cache_lock); 280 #define RELEASE_CACHE mutex_exit(&rmclomv_cache_lock); 281 static rmclomv_cache_section_t *rmclomv_cache; /* main handle-names */ 282 static rmclomv_cache_section_t *rmclomv_subcache; /* derived names */ 283 static dp_get_sysinfo_r_t rmclomv_sysinfo_data; 284 static boolean_t rmclomv_sysinfo_valid; 285 static int rmclomv_cache_valid; 286 287 extern pri_t maxclsyspri; 288 289 /* 290 * static strings 291 */ 292 static const char str_percent[] = "%"; 293 static const char str_rpm[] = " rpm"; 294 static const char str_ip_volts_ind[] = "P_PWR"; 295 static const char str_ip2_volts_ind[] = "P_PWR2"; 296 static const char str_ff_pok_ind[] = "FF_POK"; 297 static const char str_vlo_volts_ind[] = "FF_UV"; 298 static const char str_vhi_volts_ind[] = "FF_OV"; 299 static const char str_chi_amps_ind[] = "FF_OC"; 300 static const char str_chi_nr_ind[] = "FF_NR"; 301 static const char str_ot_tmpr_ind[] = "FF_OT"; 302 static const char str_fan_ind[] = "FF_FAN"; 303 static const char str_pdct_fan_ind[] = "FF_PDCT_FAN"; 304 static const char str_sc[] = "SC"; 305 306 int 307 _init(void) 308 { 309 int error = 0; 310 311 mutex_init(&rmclomv_event_hdlr_lock, NULL, MUTEX_DEFAULT, NULL); 312 mutex_init(&rmclomv_checkrmc_lock, NULL, MUTEX_DRIVER, NULL); 313 mutex_init(&rmclomv_refresh_lock, NULL, MUTEX_DRIVER, NULL); 314 mutex_init(&rmclomv_cache_lock, NULL, MUTEX_DRIVER, NULL); 315 mutex_init(&rmclomv_state_lock, NULL, MUTEX_DRIVER, NULL); 316 mutex_init(×ync_lock, NULL, MUTEX_DEFAULT, NULL); 317 cv_init(&rmclomv_checkrmc_sig_cv, NULL, CV_DRIVER, NULL); 318 cv_init(&rmclomv_refresh_sig_cv, NULL, CV_DRIVER, NULL); 319 320 error = mod_install(&modlinkage); 321 if (error) { 322 cv_destroy(&rmclomv_refresh_sig_cv); 323 cv_destroy(&rmclomv_checkrmc_sig_cv); 324 mutex_destroy(&rmclomv_state_lock); 325 mutex_destroy(&rmclomv_cache_lock); 326 mutex_destroy(&rmclomv_refresh_lock); 327 mutex_destroy(&rmclomv_checkrmc_lock); 328 mutex_destroy(&rmclomv_event_hdlr_lock); 329 } 330 return (error); 331 } 332 333 334 int 335 _info(struct modinfo *modinfop) 336 { 337 return (mod_info(&modlinkage, modinfop)); 338 } 339 340 341 int 342 _fini(void) 343 { 344 int error = 0; 345 346 error = mod_remove(&modlinkage); 347 if (error) 348 return (error); 349 cv_destroy(&rmclomv_refresh_sig_cv); 350 cv_destroy(&rmclomv_checkrmc_sig_cv); 351 mutex_destroy(×ync_lock); 352 mutex_destroy(&rmclomv_state_lock); 353 mutex_destroy(&rmclomv_cache_lock); 354 mutex_destroy(&rmclomv_refresh_lock); 355 mutex_destroy(&rmclomv_checkrmc_lock); 356 mutex_destroy(&rmclomv_event_hdlr_lock); 357 return (error); 358 } 359 360 361 /* ARGSUSED */ 362 static int 363 rmclomv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 364 { 365 minor_t m = getminor((dev_t)arg); 366 367 switch (cmd) { 368 case DDI_INFO_DEVT2DEVINFO: 369 if ((m != 0) || (rmclomv_dip == NULL)) { 370 *resultp = NULL; 371 return (DDI_FAILURE); 372 } 373 *resultp = rmclomv_dip; 374 return (DDI_SUCCESS); 375 case DDI_INFO_DEVT2INSTANCE: 376 *resultp = (void *)(uintptr_t)m; 377 return (DDI_SUCCESS); 378 default: 379 return (DDI_FAILURE); 380 } 381 } 382 383 384 static int 385 rmclomv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 386 { 387 int instance; 388 int err; 389 char *wdog_state; 390 int attaching = 1; 391 392 switch (cmd) { 393 case DDI_ATTACH: 394 /* 395 * only allow one instance 396 */ 397 instance = ddi_get_instance(dip); 398 if (instance != 0) 399 return (DDI_FAILURE); 400 401 err = ddi_create_minor_node(dip, "rmclomv", S_IFCHR, 402 instance, DDI_PSEUDO, 0); 403 if (err != DDI_SUCCESS) 404 return (DDI_FAILURE); 405 406 /* 407 * Register with rmc_comm to prevent it being detached 408 * (in the unlikely event that its attach succeeded on a 409 * platform whose platmod doesn't lock it down). 410 */ 411 err = rmc_comm_register(); 412 if (err != DDI_SUCCESS) { 413 ddi_remove_minor_node(dip, NULL); 414 return (DDI_FAILURE); 415 } 416 417 /* Remember the dev info */ 418 rmclomv_dip = dip; 419 420 /* 421 * Add the handlers which watch for unsolicited messages 422 * and post event to Sysevent Framework. 423 */ 424 err = rmclomv_add_intr_handlers(); 425 if (err != DDI_SUCCESS) { 426 rmc_comm_unregister(); 427 ddi_remove_minor_node(dip, NULL); 428 rmclomv_dip = NULL; 429 return (DDI_FAILURE); 430 } 431 432 rmclomv_checkrmc_start(); 433 rmclomv_refresh_start(); 434 435 abort_seq_handler = rmclomv_abort_seq_handler; 436 ddi_report_dev(dip); 437 438 /* 439 * Check whether we have an application watchdog 440 */ 441 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 442 DDI_PROP_DONTPASS, RMCLOMV_WATCHDOG_MODE, 443 &wdog_state) == DDI_PROP_SUCCESS) { 444 if (strcmp(wdog_state, "app") == 0) { 445 rmclomv_watchdog_mode = 1; 446 watchdog_enable = 0; 447 } 448 else 449 rmclomv_watchdog_mode = 0; 450 ddi_prop_free(wdog_state); 451 } 452 453 tod_ops.tod_set_watchdog_timer = rmc_set_watchdog_timer; 454 tod_ops.tod_clear_watchdog_timer = rmc_clear_watchdog_timer; 455 456 /* 457 * Now is a good time to activate hardware watchdog 458 * (if one exists). 459 */ 460 mutex_enter(&tod_lock); 461 if (watchdog_enable && tod_ops.tod_set_watchdog_timer != NULL) 462 err = tod_ops.tod_set_watchdog_timer(0); 463 mutex_exit(&tod_lock); 464 if (err != 0) 465 printf("Hardware watchdog enabled\n"); 466 467 /* 468 * Set time interval and start timesync routine. 469 * Also just this once set the Solaris clock 470 * to the RMC clock. 471 */ 472 timesync_interval = drv_usectohz(5*60 * MICROSEC); 473 plat_timesync((void *) &attaching); 474 475 return (DDI_SUCCESS); 476 case DDI_RESUME: 477 return (DDI_SUCCESS); 478 default: 479 return (DDI_FAILURE); 480 } 481 } 482 483 484 static int 485 rmclomv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 486 { 487 timeout_id_t tid; 488 int instance; 489 int err; 490 491 switch (cmd) { 492 case DDI_DETACH: 493 instance = ddi_get_instance(dip); 494 if (instance != 0) 495 return (DDI_FAILURE); 496 497 /* 498 * Remove the handlers which watch for unsolicited messages 499 * and post event to Sysevent Framework. 500 */ 501 err = rmclomv_remove_intr_handlers(); 502 if (err != DDI_SUCCESS) { 503 cmn_err(CE_WARN, "Failed to remove event handlers"); 504 return (DDI_FAILURE); 505 } 506 rmclomv_checkrmc_destroy(); 507 rmclomv_refresh_destroy(); 508 rmclomv_reset_cache(NULL, NULL, NULL); 509 ddi_remove_minor_node(dip, NULL); 510 511 mutex_enter(×ync_lock); 512 tid = timesync_tid; 513 timesync_tid = 0; 514 timesync_interval = 0; 515 mutex_exit(×ync_lock); 516 (void) untimeout(tid); 517 518 /* Forget the dev info */ 519 rmclomv_dip = NULL; 520 rmc_comm_unregister(); 521 return (DDI_SUCCESS); 522 case DDI_SUSPEND: 523 return (DDI_SUCCESS); 524 default: 525 return (DDI_FAILURE); 526 } 527 } 528 529 static int 530 rmclomv_add_intr_handlers() 531 { 532 int err; 533 534 if (ddi_get_soft_iblock_cookie(rmclomv_dip, DDI_SOFTINT_HIGH, 535 &rmclomv_soft_iblock_cookie) != DDI_SUCCESS) { 536 return (DDI_FAILURE); 537 } 538 err = ddi_add_softintr(rmclomv_dip, DDI_SOFTINT_HIGH, 539 &rmclomv_softintr_id, &rmclomv_soft_iblock_cookie, NULL, 540 rmclomv_break_intr, NULL); 541 if (err != DDI_SUCCESS) 542 return (DDI_FAILURE); 543 rmclomv_event_payload_msg.msg_buf = (caddr_t)&rmclomv_event_payload; 544 rmclomv_event_payload_msg.msg_len = sizeof (rmclomv_event_payload); 545 err = rmc_comm_reg_intr(DP_RMC_EVENTS, rmclomv_event_data_handler, 546 &rmclomv_event_payload_msg, NULL, &rmclomv_event_hdlr_lock); 547 if (err != 0) { 548 ddi_remove_softintr(rmclomv_softintr_id); 549 return (DDI_FAILURE); 550 } 551 return (DDI_SUCCESS); 552 } 553 554 static int 555 rmclomv_remove_intr_handlers(void) 556 { 557 int err = rmc_comm_unreg_intr(DP_RMC_EVENTS, 558 rmclomv_event_data_handler); 559 if (err != 0) { 560 cmn_err(CE_WARN, "Failed to unregister DP_RMC_EVENTS " 561 "handler. Err=%d", err); 562 return (DDI_FAILURE); 563 } 564 ddi_remove_softintr(rmclomv_softintr_id); 565 return (DDI_SUCCESS); 566 } 567 568 static void 569 rmclomv_abort_seq_handler(char *msg) 570 { 571 if (key_position == RMC_KEYSWITCH_POS_LOCKED) 572 cmn_err(CE_CONT, "KEY in LOCKED position, " 573 "ignoring debug enter sequence"); 574 else { 575 rmclomv_break_requested = B_TRUE; 576 if (msg != NULL) 577 prom_printf("%s\n", msg); 578 579 ddi_trigger_softintr(rmclomv_softintr_id); 580 } 581 } 582 583 /* ARGSUSED */ 584 static uint_t 585 rmclomv_break_intr(caddr_t arg) 586 { 587 if (rmclomv_break_requested) { 588 rmclomv_break_requested = B_FALSE; 589 debug_enter(NULL); 590 return (DDI_INTR_CLAIMED); 591 } 592 593 return (DDI_INTR_UNCLAIMED); 594 } 595 596 /* 597 * Create a cache section structure 598 */ 599 static rmclomv_cache_section_t * 600 create_cache_section(int sensor_type, int num) 601 { 602 size_t len = offsetof(rmclomv_cache_section_t, entry[0]) + 603 num * sizeof (rmclomv_cache_entry_t); 604 rmclomv_cache_section_t *ptr = kmem_zalloc(len, KM_SLEEP); 605 ptr->next_section = NULL; 606 ptr->sensor_type = sensor_type; 607 ptr->num_entries = num; 608 ptr->section_len = len; 609 return (ptr); 610 } 611 612 /* 613 * Free a cache_section. 614 */ 615 static void 616 free_section(rmclomv_cache_section_t *section) 617 { 618 size_t len = section->section_len; 619 kmem_free(section, len); 620 } 621 622 /* 623 * adds supplied section to end of cache chain 624 * must be called with cache locked 625 */ 626 static void 627 add_section(rmclomv_cache_section_t **head, rmclomv_cache_section_t *section) 628 { 629 section->next_section = *head; 630 *head = section; 631 } 632 633 /* 634 * This function releases all cache sections and exchanges the two 635 * chain heads for new values. 636 */ 637 static void 638 rmclomv_reset_cache(rmclomv_cache_section_t *new_chain, 639 rmclomv_cache_section_t *new_subchain, dp_get_sysinfo_r_t *sysinfo) 640 { 641 rmclomv_cache_section_t *first; 642 rmclomv_cache_section_t *sub_first; 643 rmclomv_cache_section_t *next; 644 645 LOCK_CACHE 646 647 rmclomv_cache_valid = (new_chain != NULL); 648 first = rmclomv_cache; 649 rmclomv_cache = new_chain; 650 sub_first = rmclomv_subcache; 651 rmclomv_subcache = new_subchain; 652 653 if (sysinfo == NULL) 654 bzero(&rmclomv_sysinfo_data, sizeof (rmclomv_sysinfo_data)); 655 else 656 bcopy(sysinfo, &rmclomv_sysinfo_data, 657 sizeof (rmclomv_sysinfo_data)); 658 659 rmclomv_sysinfo_valid = (sysinfo != NULL); 660 661 RELEASE_CACHE 662 663 while (first != NULL) { 664 next = first->next_section; 665 free_section(first); 666 first = next; 667 } 668 669 while (sub_first != NULL) { 670 next = sub_first->next_section; 671 free_section(sub_first); 672 sub_first = next; 673 } 674 } 675 676 /* 677 * cache must be locked before calling rmclomv_find_section 678 */ 679 static rmclomv_cache_section_t * 680 rmclomv_find_section(rmclomv_cache_section_t *start, uint16_t sensor) 681 { 682 rmclomv_cache_section_t *next = start; 683 684 while ((next != NULL) && (next->sensor_type != sensor)) 685 next = next->next_section; 686 687 return (next); 688 } 689 690 /* 691 * Return a string presenting the keyswitch position 692 * For unknown values returns "Unknown" 693 */ 694 static char * 695 rmclomv_key_position(enum rmc_keyswitch_pos pos) 696 { 697 switch (pos) { 698 699 case RMC_KEYSWITCH_POS_NORMAL: 700 return ("NORMAL"); 701 case RMC_KEYSWITCH_POS_DIAG: 702 return ("DIAG"); 703 case RMC_KEYSWITCH_POS_LOCKED: 704 return ("LOCKED"); 705 case RMC_KEYSWITCH_POS_OFF: 706 return ("STBY"); 707 default: 708 return ("UNKNOWN"); 709 } 710 } 711 712 /* 713 * The sensor id name is sought in the supplied section and if found 714 * its index within the section is written to *index. 715 * Return value is zero for success, otherwise -1. 716 * The cache must be locked before calling get_sensor_by_name 717 */ 718 static int 719 get_sensor_by_name(const rmclomv_cache_section_t *section, 720 const char *name, int *index) 721 { 722 int i; 723 724 for (i = 0; i < section->num_entries; i++) { 725 if (strcmp(name, section->entry[i].handle_name.name) == 0) { 726 *index = i; 727 return (0); 728 } 729 } 730 731 *index = 0; 732 return (-1); 733 } 734 735 /* 736 * fills in the envmon_handle name 737 * if it is unknown (not cached), the dp_handle_t is returned as a hex-digit 738 * string 739 */ 740 static void 741 rmclomv_hdl_to_envhdl(dp_handle_t hdl, envmon_handle_t *envhdl) 742 { 743 rmclomv_cache_section_t *next; 744 int i; 745 746 LOCK_CACHE 747 748 for (next = rmclomv_cache; next != NULL; next = next->next_section) { 749 for (i = 0; i < next->num_entries; i++) { 750 if (next->entry[i].handle == hdl) { 751 *envhdl = next->entry[i].handle_name; 752 RELEASE_CACHE 753 return; 754 } 755 } 756 } 757 758 /* 759 * Sought handle not currently cached. 760 */ 761 RELEASE_CACHE 762 763 (void) snprintf(envhdl->name, sizeof (envhdl->name), 764 "Unknown SC node 0x%x", hdl); 765 } 766 767 static void 768 rmclomv_dr_data_handler(const char *fru_name, int hint) 769 { 770 int err = 0; 771 nvlist_t *attr_list; 772 char attach_pnt[MAXPATHLEN]; 773 774 (void) snprintf(attach_pnt, sizeof (attach_pnt), "%s", fru_name); 775 776 err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP); 777 if (err != 0) { 778 cmn_err(CE_WARN, 779 "Failed to allocate name-value list for %s event", EC_DR); 780 return; 781 } 782 783 err = nvlist_add_string(attr_list, DR_AP_ID, attach_pnt); 784 if (err != 0) { 785 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 786 DR_AP_ID, EC_DR); 787 nvlist_free(attr_list); 788 return; 789 } 790 791 /* 792 * Add the hint 793 */ 794 err = nvlist_add_string(attr_list, DR_HINT, SE_HINT2STR(hint)); 795 if (err != 0) { 796 cmn_err(CE_WARN, "Failed to add attr [%s] for %s event", 797 DR_HINT, EC_DR); 798 nvlist_free(attr_list); 799 return; 800 } 801 802 err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_DR, 803 ESC_DR_AP_STATE_CHANGE, attr_list, NULL, DDI_NOSLEEP); 804 if (err != 0) { 805 cmn_err(CE_WARN, "Failed to log %s/%s event", 806 DR_AP_ID, EC_DR); 807 } 808 809 nvlist_free(attr_list); 810 } 811 812 static void 813 fan_sysevent(char *fru_name, char *sensor_name, int sub_event) 814 { 815 nvlist_t *attr_list; 816 char fan_str[MAXNAMELEN]; 817 int err; 818 819 err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP); 820 if (err != 0) { 821 cmn_err(CE_WARN, 822 "Failed to allocate name-value list for %s/%s event", 823 EC_ENV, ESC_ENV_FAN); 824 return; 825 } 826 827 err = nvlist_add_string(attr_list, ENV_FRU_ID, fru_name); 828 if (err != 0) { 829 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 830 ENV_FRU_ID, EC_ENV, ESC_ENV_FAN); 831 nvlist_free(attr_list); 832 return; 833 } 834 835 err = nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, sensor_name); 836 if (err != 0) { 837 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 838 ENV_FRU_RESOURCE_ID, EC_ENV, ESC_ENV_FAN); 839 nvlist_free(attr_list); 840 return; 841 } 842 843 err = nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR); 844 if (err != 0) { 845 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 846 ENV_FRU_DEVICE, EC_ENV, ESC_ENV_FAN); 847 nvlist_free(attr_list); 848 return; 849 } 850 851 err = nvlist_add_int32(attr_list, ENV_FRU_STATE, 852 (sub_event == RMC_ENV_FAULT_EVENT) ? ENV_FAILED : ENV_OK); 853 if (err != 0) { 854 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 855 ENV_FRU_STATE, EC_ENV, ESC_ENV_FAN); 856 nvlist_free(attr_list); 857 return; 858 } 859 860 if (sub_event == RMC_ENV_FAULT_EVENT) { 861 (void) snprintf(fan_str, sizeof (fan_str), 862 "fan %s/%s is now failed", fru_name, sensor_name); 863 } else { 864 (void) snprintf(fan_str, sizeof (fan_str), 865 "fan %s/%s is now ok", fru_name, sensor_name); 866 } 867 err = nvlist_add_string(attr_list, ENV_MSG, fan_str); 868 if (err != 0) { 869 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 870 ENV_MSG, EC_ENV, ESC_ENV_FAN); 871 nvlist_free(attr_list); 872 return; 873 } 874 875 err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_ENV, 876 ESC_ENV_FAN, attr_list, NULL, DDI_NOSLEEP); 877 if (err != 0) { 878 cmn_err(CE_WARN, "Failed to log %s/%s event", 879 EC_ENV, ESC_ENV_FAN); 880 } 881 882 cmn_err(CE_NOTE, "%s", fan_str); 883 nvlist_free(attr_list); 884 } 885 886 static void 887 threshold_sysevent(char *fru_name, char *sensor_name, int sub_event, 888 char event_type) 889 { 890 nvlist_t *attr_list; 891 int err; 892 char *subclass; 893 char sensor_str[MAXNAMELEN]; 894 895 subclass = (event_type == 'T') ? ESC_ENV_TEMP : ESC_ENV_POWER; 896 897 err = nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP); 898 if (err != 0) { 899 cmn_err(CE_WARN, 900 "Failed to allocate name-value list for %s/%s event", 901 EC_ENV, subclass); 902 return; 903 } 904 905 err = nvlist_add_string(attr_list, ENV_FRU_ID, fru_name); 906 if (err != 0) { 907 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 908 ENV_FRU_ID, EC_ENV, subclass); 909 nvlist_free(attr_list); 910 return; 911 } 912 913 err = nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, sensor_name); 914 if (err != 0) { 915 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 916 ENV_FRU_RESOURCE_ID, EC_ENV, subclass); 917 nvlist_free(attr_list); 918 return; 919 } 920 921 err = nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR); 922 if (err != 0) { 923 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 924 ENV_FRU_DEVICE, EC_ENV, subclass); 925 nvlist_free(attr_list); 926 return; 927 } 928 929 switch (sub_event) { 930 case RMC_ENV_OK_EVENT: 931 err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_OK); 932 break; 933 case RMC_ENV_WARNING_THRESHOLD_EVENT: 934 err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_WARNING); 935 break; 936 case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT: 937 err = nvlist_add_int32(attr_list, ENV_FRU_STATE, ENV_FAILED); 938 break; 939 } 940 if (err != 0) { 941 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 942 ENV_FRU_STATE, EC_ENV, subclass); 943 nvlist_free(attr_list); 944 return; 945 } 946 947 switch (sub_event) { 948 case RMC_ENV_OK_EVENT: 949 (void) snprintf(sensor_str, sizeof (sensor_str), 950 "sensor %s/%s is now ok", fru_name, 951 sensor_name); 952 break; 953 case RMC_ENV_WARNING_THRESHOLD_EVENT: 954 (void) snprintf(sensor_str, sizeof (sensor_str), 955 "sensor %s/%s is now outside warning thresholds", fru_name, 956 sensor_name); 957 break; 958 case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT: 959 (void) snprintf(sensor_str, sizeof (sensor_str), 960 "sensor %s/%s is now outside shutdown thresholds", fru_name, 961 sensor_name); 962 break; 963 } 964 err = nvlist_add_string(attr_list, ENV_MSG, sensor_str); 965 if (err != 0) { 966 cmn_err(CE_WARN, "Failed to add attr [%s] for %s/%s event", 967 ENV_MSG, EC_ENV, subclass); 968 nvlist_free(attr_list); 969 return; 970 } 971 972 err = ddi_log_sysevent(rmclomv_dip, DDI_VENDOR_SUNW, EC_ENV, 973 subclass, attr_list, NULL, DDI_NOSLEEP); 974 if (err != 0) { 975 cmn_err(CE_WARN, "Failed to log %s/%s event", 976 EC_ENV, subclass); 977 } 978 979 cmn_err(CE_NOTE, "%s", sensor_str); 980 nvlist_free(attr_list); 981 } 982 983 static uint_t 984 rmclomv_event_data_handler(char *arg) 985 { 986 dp_event_notification_t *payload; 987 rmc_comm_msg_t *msg; 988 envmon_handle_t envhdl; 989 int hint; 990 char *ptr, *save_ptr; 991 992 if (arg == NULL) { 993 return (DDI_INTR_CLAIMED); 994 } 995 996 msg = (rmc_comm_msg_t *)arg; 997 if (msg->msg_buf == NULL) { 998 return (DDI_INTR_CLAIMED); 999 } 1000 1001 payload = (dp_event_notification_t *)msg->msg_buf; 1002 switch (payload->event) { 1003 1004 case RMC_KEYSWITCH_EVENT: 1005 real_key_position = payload->event_info.ev_keysw.key_position; 1006 cmn_err(CE_NOTE, "keyswitch change event - state = %s", 1007 rmclomv_key_position(real_key_position)); 1008 if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) && 1009 (real_key_position <= RMC_KEYSWITCH_POS_OFF)) { 1010 key_position = real_key_position; 1011 } else { 1012 /* treat unknown key position as locked */ 1013 key_position = RMC_KEYSWITCH_POS_LOCKED; 1014 } 1015 break; 1016 1017 case RMC_HPU_EVENT: 1018 /* 1019 * send appropriate sysevent 1020 */ 1021 switch (payload->event_info.ev_hpunot.sub_event) { 1022 case RMC_HPU_REMOVE_EVENT: 1023 hint = SE_HINT_REMOVE; 1024 break; 1025 case RMC_HPU_INSERT_EVENT: 1026 hint = SE_HINT_INSERT; 1027 break; 1028 default: 1029 hint = SE_NO_HINT; 1030 break; 1031 } 1032 rmclomv_hdl_to_envhdl(payload->event_info.ev_hpunot.hpu_hdl, 1033 &envhdl); 1034 rmclomv_dr_data_handler(envhdl.name, hint); 1035 break; 1036 1037 case RMC_INIT_EVENT: 1038 /* 1039 * Wake up the refresh thread. 1040 */ 1041 rmclomv_refresh_wakeup(); 1042 1043 /* 1044 * Wake up the checkrmc thread for an early indication to PICL 1045 */ 1046 rmclomv_checkrmc_wakeup(NULL); 1047 break; 1048 1049 case RMC_ENV_EVENT: 1050 rmclomv_hdl_to_envhdl(payload->event_info.ev_envnot.env_hdl, 1051 &envhdl); 1052 1053 /* split name into fru name and sensor name */ 1054 ptr = strchr(envhdl.name, '.'); 1055 1056 /* must have at least one '.' */ 1057 if (ptr == NULL) 1058 break; 1059 1060 /* find last '.' - convert the others to '/' */ 1061 for (;;) { 1062 save_ptr = ptr; 1063 ptr = strchr(ptr, '.'); 1064 if (ptr == NULL) { 1065 ptr = save_ptr; 1066 break; 1067 } 1068 *save_ptr = '/'; 1069 } 1070 *ptr = '\0'; 1071 ptr++; 1072 /* is it a voltage or temperature sensor? */ 1073 if ((*ptr == 'V' || *ptr == 'T') && *(ptr + 1) == '_') { 1074 switch (payload->event_info.ev_envnot.sub_event) { 1075 case RMC_ENV_WARNING_THRESHOLD_EVENT: 1076 case RMC_ENV_SHUTDOWN_THRESHOLD_EVENT: 1077 case RMC_ENV_OK_EVENT: 1078 threshold_sysevent(envhdl.name, ptr, 1079 payload->event_info.ev_envnot.sub_event, 1080 *ptr); 1081 break; 1082 default: 1083 break; 1084 } 1085 } 1086 1087 /* 1088 * is it a fan sensor? 1089 * Fan sensor names end either in RS, F0 or F1 1090 */ 1091 if ((*ptr == 'R' && *(ptr + 1) == 'S' && *(ptr + 2) == '\0') || 1092 (*ptr == 'F' && *(ptr + 1) == '0' && *(ptr + 2) == '\0') || 1093 (*ptr == 'F' && *(ptr + 1) == '1' && *(ptr + 2) == '\0')) { 1094 switch (payload->event_info.ev_envnot.sub_event) { 1095 case RMC_ENV_FAULT_EVENT: 1096 case RMC_ENV_OK_EVENT: 1097 fan_sysevent(envhdl.name, ptr, 1098 payload->event_info.ev_envnot.sub_event); 1099 break; 1100 default: 1101 break; 1102 } 1103 } 1104 break; 1105 1106 case RMC_LOG_EVENT: 1107 { 1108 int level = 10; 1109 int flags = SL_NOTE | SL_CONSOLE; 1110 char *message = 1111 (char *)payload->event_info.ev_rmclog.log_record; 1112 1113 message[ payload->event_info.ev_rmclog.log_record_size] = '\0'; 1114 1115 /* 1116 * Logs have a 10 character prefix - specifying the severity of 1117 * the event being logged. Thus all the magic number 10s down 1118 * here 1119 */ 1120 if (0 == strncmp("CRITICAL: ", message, 10)) { 1121 message += 10; 1122 level = 0; 1123 flags = SL_FATAL | SL_ERROR | SL_CONSOLE; 1124 } else if (0 == strncmp("MAJOR: ", message, 10)) { 1125 message += 10; 1126 level = 5; 1127 flags = SL_WARN | SL_ERROR | SL_CONSOLE; 1128 } else if (0 == strncmp("MINOR: ", message, 10)) { 1129 message += 10; 1130 level = 10; 1131 flags = SL_NOTE | SL_CONSOLE; 1132 } 1133 1134 (void) strlog(0, 0, level, flags, message); 1135 break; 1136 } 1137 1138 default: 1139 return (DDI_INTR_CLAIMED); 1140 } 1141 1142 return (DDI_INTR_CLAIMED); 1143 } 1144 1145 /*ARGSUSED*/ 1146 static int 1147 rmclomv_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p) 1148 { 1149 int error = 0; 1150 int instance = getminor(*dev_p); 1151 1152 if (instance != 0) 1153 return (ENXIO); 1154 1155 if ((flag & FWRITE) != 0 && (error = drv_priv(cred_p)) != 0) 1156 return (error); 1157 1158 return (0); 1159 } 1160 1161 /*ARGSUSED*/ 1162 static int 1163 rmclomv_close(dev_t dev, int flag, int otyp, cred_t *cred_p) 1164 { 1165 return (DDI_SUCCESS); 1166 } 1167 1168 static int 1169 rmclomv_do_cmd(int req_cmd, int resp_cmd, int resp_len, intptr_t arg_req, 1170 intptr_t arg_res) 1171 { 1172 rmc_comm_msg_t request, *reqp = &request; 1173 rmc_comm_msg_t response, *resp = &response; 1174 int rv = 0; 1175 1176 bzero((caddr_t)&request, sizeof (request)); 1177 reqp->msg_type = req_cmd; 1178 reqp->msg_buf = (caddr_t)arg_req; 1179 bzero((caddr_t)&response, sizeof (response)); 1180 resp->msg_type = resp_cmd; 1181 resp->msg_buf = (caddr_t)arg_res; 1182 resp->msg_len = resp_len; 1183 1184 switch (req_cmd) { 1185 case DP_GET_SYSINFO: 1186 resp->msg_len = sizeof (dp_get_sysinfo_r_t); 1187 break; 1188 case DP_GET_EVENT_LOG: 1189 resp->msg_len = sizeof (dp_get_event_log_r_t); 1190 break; 1191 case DP_GET_VOLTS: 1192 reqp->msg_len = sizeof (dp_get_volts_t); 1193 break; 1194 case DP_GET_TEMPERATURES: 1195 reqp->msg_len = sizeof (dp_get_temperatures_t); 1196 break; 1197 case DP_GET_CIRCUIT_BRKS: 1198 reqp->msg_len = sizeof (dp_get_circuit_brks_t); 1199 break; 1200 case DP_GET_FAN_STATUS: 1201 reqp->msg_len = sizeof (dp_get_fan_status_t); 1202 break; 1203 case DP_GET_PSU_STATUS: 1204 reqp->msg_len = sizeof (dp_get_psu_status_t); 1205 break; 1206 case DP_GET_LED_STATE: 1207 reqp->msg_len = sizeof (dp_get_led_state_t); 1208 break; 1209 case DP_SET_LED_STATE: 1210 reqp->msg_len = sizeof (dp_set_led_state_t); 1211 break; 1212 case DP_GET_FRU_STATUS: 1213 reqp->msg_len = sizeof (dp_get_fru_status_t); 1214 break; 1215 case DP_GET_HANDLE_NAME: 1216 reqp->msg_len = sizeof (dp_get_handle_name_t); 1217 break; 1218 case DP_GET_ALARM_STATE: 1219 reqp->msg_len = sizeof (dp_get_alarm_state_t); 1220 break; 1221 case DP_SET_ALARM_STATE: 1222 reqp->msg_len = sizeof (dp_set_alarm_state_t); 1223 break; 1224 case DP_GET_SDP_VERSION: 1225 resp->msg_len = sizeof (dp_get_sdp_version_r_t); 1226 break; 1227 case DP_GET_CHASSIS_SERIALNUM: 1228 reqp->msg_len = 0; 1229 break; 1230 case DP_GET_DATE_TIME: 1231 reqp->msg_len = 0; 1232 break; 1233 default: 1234 return (EINVAL); 1235 } 1236 1237 rv = rmc_comm_request_response(reqp, resp, 1238 RMCLOMV_DEFAULT_MAX_MBOX_WAIT_TIME); 1239 1240 if (rv != RCNOERR) { 1241 /* 1242 * RMC returned an error or failed to respond. 1243 * Where the RMC itself is implicated, rmclomv_rmc_error 1244 * is set non-zero. It is cleared after an error free exchange. 1245 * Two failure cases are distinguished: 1246 * RMCLOMV_RMCSTATE_FAILED and RMCLOMV_RMCSTATE_DOWNLOAD. 1247 */ 1248 switch (rv) { 1249 case RCENOSOFTSTATE: 1250 /* invalid/NULL soft state structure */ 1251 return (EIO); 1252 case RCENODATALINK: 1253 /* 1254 * firmware download in progress, 1255 * can you come back later? 1256 */ 1257 rmclomv_rmc_error = RMCLOMV_RMCSTATE_DOWNLOAD; 1258 rmclomv_rmc_state = RMCLOMV_RMCSTATE_DOWNLOAD; 1259 return (EAGAIN); 1260 case RCENOMEM: 1261 /* memory problems */ 1262 return (ENOMEM); 1263 case RCECANTRESEND: 1264 /* resend failed */ 1265 rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED; 1266 return (EIO); 1267 case RCEMAXRETRIES: 1268 /* reply not received - retries exceeded */ 1269 rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED; 1270 return (EINTR); 1271 case RCETIMEOUT: 1272 /* reply not received - command has timed out */ 1273 rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED; 1274 return (EINTR); 1275 case RCEINVCMD: 1276 /* data protocol cmd not supported */ 1277 return (ENOTSUP); 1278 case RCEINVARG: 1279 /* invalid argument(s) */ 1280 return (ENOTSUP); 1281 case RCEGENERIC: 1282 /* generic error */ 1283 rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED; 1284 return (EIO); 1285 default: 1286 rmclomv_rmc_error = RMCLOMV_RMCSTATE_FAILED; 1287 return (EIO); 1288 } 1289 } 1290 1291 rmclomv_rmc_error = RMCLOMV_RMCERROR_NONE; 1292 return (0); 1293 } 1294 1295 /* 1296 * validate_section_entry checks that the entry at the specified index 1297 * is valid and not duplicated by an entry above. If these tests fail 1298 * the entry is removed and B_FALSE returned. Otherwise returns B_TRUE. 1299 */ 1300 static int 1301 validate_section_entry(rmclomv_cache_section_t *section, int index) 1302 { 1303 int i; 1304 rmclomv_cache_entry_t *entry; 1305 1306 for (i = index; i < section->num_entries; i++) { 1307 entry = §ion->entry[i]; 1308 if (entry->handle_name.name[0] == '\0') { 1309 cmn_err(CE_WARN, 1310 "rmclomv: empty handle_name, handle 0x%x type %x", 1311 entry->handle, section->sensor_type); 1312 } else if (entry->ind_mask != 0) { 1313 continue; /* skip special entries */ 1314 } else if (entry->handle == DP_NULL_HANDLE) { 1315 cmn_err(CE_WARN, 1316 "rmclomv: null handle id for \"%s\" type %x", 1317 entry->handle_name.name, section->sensor_type); 1318 } else if (i == index) { 1319 continue; 1320 } else if (section->entry[index].handle == entry->handle) { 1321 cmn_err(CE_WARN, 1322 "rmclomv: duplicate handle 0x%x type %x", 1323 entry->handle, section->sensor_type); 1324 } else if (strcmp(entry->handle_name.name, 1325 section->entry[index].handle_name.name) == 0) { 1326 cmn_err(CE_WARN, 1327 "rmclomv: duplicate handle_name \"%s\", " 1328 "handle 0x%x type %x", entry->handle_name.name, 1329 entry->handle, section->sensor_type); 1330 } else 1331 continue; 1332 1333 /* 1334 * need to remove the entry at index 1335 */ 1336 section->num_entries--; 1337 1338 for (i = index; i < section->num_entries; i++) { 1339 section->entry[i] = section->entry[i + 1]; 1340 } 1341 1342 return (B_FALSE); 1343 } 1344 1345 return (B_TRUE); 1346 } 1347 1348 /* 1349 * Populate a section containing handles with corresponding names 1350 * The supplied section structure must not be publically visible and the 1351 * name cache must not be locked either (because RMC i/o is required). 1352 * 1353 * This is the place where a sanity check is applied. Entries containing 1354 * duplicate handles, duplicate names or empty names are removed and the 1355 * structure is compacted. As a result num_entries may be reduced. 1356 */ 1357 static int 1358 add_names_to_section(rmclomv_cache_section_t *section) 1359 { 1360 int retval = 0; 1361 int ditched = B_FALSE; 1362 int index; 1363 dp_get_handle_name_r_t handle_name_r; 1364 rmclomv_cache_entry_t *entry; 1365 1366 for (index = 0; index < section->num_entries; index++) { 1367 entry = §ion->entry[index]; 1368 if (entry->ind_mask != 0) 1369 continue; /* skip special entries */ 1370 handle_name_r.handle = entry->handle; 1371 retval = rmclomv_do_cmd(DP_GET_HANDLE_NAME, 1372 DP_GET_HANDLE_NAME_R, sizeof (handle_name_r), 1373 (intptr_t)&handle_name_r, (intptr_t)&handle_name_r); 1374 if (retval == 0) 1375 bcopy(handle_name_r.name, 1376 entry->handle_name.name, DP_MAX_HANDLE_NAME); 1377 } 1378 1379 /* 1380 * now ditch invalid and duplicate entries 1381 */ 1382 for (index = 0; index < section->num_entries; index++) { 1383 while (validate_section_entry(section, index) == B_FALSE) 1384 ditched = B_TRUE; 1385 } 1386 1387 if (ditched) 1388 cmn_err(CE_WARN, "Retaining %d nodes of type %d", 1389 section->num_entries, section->sensor_type); 1390 1391 return (retval); 1392 } 1393 1394 /* 1395 * The supplied (PSU) cache section is traversed and entries are created 1396 * for the individual indicators belonging to a PSU. These entries are 1397 * placed in a private chain. The caller, subsequently acquires the 1398 * cache lock and copies the chain head to make it public. 1399 * The handle-names for PSU indicators are derived from the parent PSU 1400 * handle-name. 1401 * NOTE: add_names_to_section() may have reduced psu_section->num_entries 1402 * so DON'T USE psu_resp->num_psus 1403 */ 1404 static void 1405 make_psu_subsections(rmclomv_cache_section_t *psu_section, 1406 rmclomv_cache_section_t **chain_head, dp_get_psu_status_r_t *psu_resp) 1407 { 1408 int index; 1409 int subindex = 0; 1410 rmclomv_cache_section_t *subsection; 1411 rmclomv_cache_entry_t *src_entry; 1412 rmclomv_cache_entry_t *dst_entry; 1413 1414 subsection = create_cache_section(RMCLOMV_VOLT_IND, 1415 RMCLOMV_MAX_VI_PER_PSU * psu_section->num_entries); 1416 for (index = 0; index < psu_section->num_entries; index++) { 1417 src_entry = &psu_section->entry[index]; 1418 if ((psu_resp->psu_status[index].mask & 1419 DP_PSU_INPUT_STATUS) != 0) { 1420 dst_entry = &subsection->entry[subindex++]; 1421 dst_entry->handle = src_entry->handle; 1422 dst_entry->ind_mask = DP_PSU_INPUT_STATUS; 1423 (void) snprintf(dst_entry->handle_name.name, 1424 ENVMON_MAXNAMELEN, "%s.%s", 1425 src_entry->handle_name.name, 1426 str_ip_volts_ind); 1427 } 1428 1429 if ((psu_resp->psu_status[index].mask & 1430 DP_PSU_SEC_INPUT_STATUS) != 0) { 1431 dst_entry = &subsection->entry[subindex++]; 1432 dst_entry->handle = src_entry->handle; 1433 dst_entry->ind_mask = DP_PSU_SEC_INPUT_STATUS; 1434 (void) snprintf(dst_entry->handle_name.name, 1435 ENVMON_MAXNAMELEN, "%s.%s", 1436 src_entry->handle_name.name, 1437 str_ip2_volts_ind); 1438 } 1439 1440 if ((psu_resp->psu_status[index].mask & 1441 DP_PSU_OUTPUT_STATUS) != 0) { 1442 dst_entry = &subsection->entry[subindex++]; 1443 dst_entry->handle = src_entry->handle; 1444 dst_entry->ind_mask = DP_PSU_OUTPUT_STATUS; 1445 (void) snprintf(dst_entry->handle_name.name, 1446 ENVMON_MAXNAMELEN, "%s.%s", 1447 src_entry->handle_name.name, 1448 str_ff_pok_ind); 1449 } 1450 1451 if ((psu_resp->psu_status[index].mask & 1452 DP_PSU_OUTPUT_VLO_STATUS) != 0) { 1453 dst_entry = &subsection->entry[subindex++]; 1454 dst_entry->handle = src_entry->handle; 1455 dst_entry->ind_mask = DP_PSU_OUTPUT_VLO_STATUS; 1456 (void) snprintf(dst_entry->handle_name.name, 1457 ENVMON_MAXNAMELEN, "%s.%s", 1458 src_entry->handle_name.name, 1459 str_vlo_volts_ind); 1460 } 1461 1462 if ((psu_resp->psu_status[index].mask & 1463 DP_PSU_OUTPUT_VHI_STATUS) != 0) { 1464 dst_entry = &subsection->entry[subindex++]; 1465 dst_entry->handle = src_entry->handle; 1466 dst_entry->ind_mask = DP_PSU_OUTPUT_VHI_STATUS; 1467 (void) snprintf(dst_entry->handle_name.name, 1468 ENVMON_MAXNAMELEN, "%s.%s", 1469 src_entry->handle_name.name, 1470 str_vhi_volts_ind); 1471 } 1472 } 1473 /* 1474 * Adjust number of entries value in cache section 1475 * to match the facts. 1476 */ 1477 subsection->num_entries = subindex; 1478 add_section(chain_head, subsection); 1479 1480 subsection = create_cache_section(RMCLOMV_AMP_IND, 1481 RMCLOMV_MAX_CI_PER_PSU * psu_section->num_entries); 1482 subindex = 0; 1483 for (index = 0; index < psu_section->num_entries; index++) { 1484 int mask = psu_resp->psu_status[index].mask; 1485 src_entry = &psu_section->entry[index]; 1486 if ((mask & DP_PSU_OUTPUT_AHI_STATUS) != 0) { 1487 dst_entry = &subsection->entry[subindex++]; 1488 dst_entry->handle = src_entry->handle; 1489 dst_entry->ind_mask = DP_PSU_OUTPUT_AHI_STATUS; 1490 (void) snprintf(dst_entry->handle_name.name, 1491 ENVMON_MAXNAMELEN, "%s.%s", 1492 src_entry->handle_name.name, 1493 str_chi_amps_ind); 1494 } 1495 if ((mask & DP_PSU_NR_WARNING) != 0) { 1496 dst_entry = &subsection->entry[subindex++]; 1497 dst_entry->handle = src_entry->handle; 1498 dst_entry->ind_mask = DP_PSU_NR_WARNING; 1499 (void) snprintf(dst_entry->handle_name.name, 1500 ENVMON_MAXNAMELEN, "%s.%s", 1501 src_entry->handle_name.name, 1502 str_chi_nr_ind); 1503 } 1504 } 1505 subsection->num_entries = subindex; 1506 add_section(chain_head, subsection); 1507 1508 subsection = create_cache_section(RMCLOMV_TEMP_IND, 1509 psu_section->num_entries); 1510 subindex = 0; 1511 for (index = 0; index < psu_section->num_entries; index++) { 1512 if ((psu_resp->psu_status[index].mask & 1513 DP_PSU_OVERTEMP_FAULT) != 0) { 1514 src_entry = &psu_section->entry[index]; 1515 dst_entry = &subsection->entry[subindex++]; 1516 dst_entry->handle = src_entry->handle; 1517 dst_entry->ind_mask = DP_PSU_OVERTEMP_FAULT; 1518 (void) snprintf(dst_entry->handle_name.name, 1519 ENVMON_MAXNAMELEN, "%s.%s", 1520 src_entry->handle_name.name, 1521 str_ot_tmpr_ind); 1522 } 1523 } 1524 subsection->num_entries = subindex; 1525 add_section(chain_head, subsection); 1526 1527 subsection = create_cache_section(RMCLOMV_FAN_IND, 1528 RMCLOMV_MAX_FI_PER_PSU * psu_section->num_entries); 1529 subindex = 0; 1530 for (index = 0; index < psu_section->num_entries; index++) { 1531 int mask = psu_resp->psu_status[index].mask; 1532 src_entry = &psu_section->entry[index]; 1533 if ((mask & DP_PSU_FAN_FAULT) != 0) { 1534 dst_entry = &subsection->entry[subindex++]; 1535 dst_entry->handle = src_entry->handle; 1536 dst_entry->ind_mask = DP_PSU_FAN_FAULT; 1537 (void) snprintf(dst_entry->handle_name.name, 1538 ENVMON_MAXNAMELEN, "%s.%s", 1539 src_entry->handle_name.name, str_fan_ind); 1540 } 1541 if ((mask & DP_PSU_PDCT_FAN) != 0) { 1542 dst_entry = &subsection->entry[subindex++]; 1543 dst_entry->handle = src_entry->handle; 1544 dst_entry->ind_mask = DP_PSU_PDCT_FAN; 1545 (void) snprintf(dst_entry->handle_name.name, 1546 ENVMON_MAXNAMELEN, "%s.%s", 1547 src_entry->handle_name.name, str_pdct_fan_ind); 1548 } 1549 } 1550 subsection->num_entries = subindex; 1551 add_section(chain_head, subsection); 1552 } 1553 1554 static void 1555 refresh_name_cache(int force_fail) 1556 { 1557 union { 1558 dp_get_volts_t u_volts_cmd; 1559 dp_get_temperatures_t u_temp_cmd; 1560 dp_get_circuit_brks_t u_ampi_cmd; 1561 dp_get_fan_status_t u_fan_cmd; 1562 dp_get_psu_status_t u_psu_cmd; 1563 dp_get_fru_status_t u_fru_cmd; 1564 dp_get_led_state_t u_led_cmd; 1565 dp_set_led_state_t u_setled_cmd; 1566 dp_get_alarm_state_t u_alarm_cmd; 1567 dp_set_alarm_state_t u_setalarm_cmd; 1568 } rmc_cmdbuf; 1569 1570 /* defines for accessing union fields */ 1571 #define volts_cmd rmc_cmdbuf.u_volts_cmd 1572 #define temp_cmd rmc_cmdbuf.u_temp_cmd 1573 #define ampi_cmd rmc_cmdbuf.u_ampi_cmd 1574 #define fan_cmd rmc_cmdbuf.u_fan_cmd 1575 #define psu_cmd rmc_cmdbuf.u_psu_cmd 1576 #define fru_cmd rmc_cmdbuf.u_fru_cmd 1577 #define led_cmd rmc_cmdbuf.u_led_cmd 1578 #define setled_cmd rmc_cmdbuf.u_setled_cmd 1579 #define alarm_cmd rmc_cmdbuf.u_alarm_cmd 1580 #define setalarm_cmd rmc_cmdbuf.u_setalarm_cmd 1581 1582 /* 1583 * Data area to read sensor data into 1584 */ 1585 static union { 1586 char reservation[RMCRESBUFLEN]; 1587 dp_get_volts_r_t u_volts_r; 1588 dp_get_temperatures_r_t u_temp_r; 1589 dp_get_circuit_brks_r_t u_ampi_r; 1590 dp_get_fan_status_r_t u_fan_r; 1591 dp_get_psu_status_r_t u_psu_r; 1592 dp_get_fru_status_r_t u_fru_r; 1593 dp_get_led_state_r_t u_led_r; 1594 dp_set_led_state_r_t u_setled_r; 1595 dp_get_alarm_state_r_t u_alarm_r; 1596 dp_set_alarm_state_r_t u_setalarm_r; 1597 } rmc_sensbuf; 1598 1599 /* defines for accessing union fields */ 1600 #define volts_r rmc_sensbuf.u_volts_r 1601 #define temp_r rmc_sensbuf.u_temp_r 1602 #define ampi_r rmc_sensbuf.u_ampi_r 1603 #define fan_r rmc_sensbuf.u_fan_r 1604 #define psu_r rmc_sensbuf.u_psu_r 1605 #define fru_r rmc_sensbuf.u_fru_r 1606 #define led_r rmc_sensbuf.u_led_r 1607 #define setled_r rmc_sensbuf.u_setled_r 1608 #define alarm_r rmc_sensbuf.u_alarm_r 1609 #define setalarm_r rmc_sensbuf.u_setalarm_r 1610 1611 int retval = force_fail; 1612 int retval1 = retval; 1613 int index; 1614 rmclomv_cache_section_t *my_chain = NULL; 1615 rmclomv_cache_section_t *derived_chain = NULL; 1616 rmclomv_cache_section_t *section; 1617 rmclomv_cache_section_t *psu_section; 1618 rmclomv_cache_section_t *fru_section; 1619 dp_get_sysinfo_r_t sysinfo; 1620 rmclomv_cache_entry_t *entry; 1621 1622 if (retval == 0) { 1623 retval = rmclomv_do_cmd(DP_GET_SYSINFO, DP_GET_SYSINFO_R, 1624 sizeof (sysinfo), (intptr_t)NULL, (intptr_t)&sysinfo); 1625 } 1626 if (retval == 0) { 1627 fru_cmd.handle = DP_NULL_HANDLE; 1628 retval = rmclomv_do_cmd(DP_GET_FRU_STATUS, DP_GET_FRU_STATUS_R, 1629 RMCRESBUFLEN, (intptr_t)&fru_cmd, (intptr_t)&fru_r); 1630 } 1631 if (retval != 0) 1632 fru_r.num_frus = 0; 1633 1634 /* 1635 * Reserve space for special additional entries in the FRU section 1636 */ 1637 fru_section = create_cache_section(RMCLOMV_HPU_IND, 1638 RMCLOMV_NUM_SPECIAL_FRUS + fru_r.num_frus); 1639 1640 /* 1641 * add special entry for RMC itself 1642 */ 1643 entry = &fru_section->entry[0]; 1644 (void) snprintf(entry->handle_name.name, sizeof (envmon_handle_t), 1645 "SC"); 1646 entry->handle = 0; 1647 entry->ind_mask = 1; /* flag as a special entry */ 1648 1649 /* 1650 * populate any other FRU entries 1651 */ 1652 for (index = 0; index < fru_r.num_frus; index++) { 1653 fru_section->entry[RMCLOMV_NUM_SPECIAL_FRUS + index].handle = 1654 fru_r.fru_status[index].handle; 1655 fru_section->entry[RMCLOMV_NUM_SPECIAL_FRUS + index].ind_mask = 1656 0; 1657 } 1658 1659 my_chain = fru_section; 1660 1661 if (retval == 0) { 1662 volts_cmd.handle = DP_NULL_HANDLE; 1663 retval = rmclomv_do_cmd(DP_GET_VOLTS, DP_GET_VOLTS_R, 1664 RMCRESBUFLEN, (intptr_t)&volts_cmd, (intptr_t)&volts_r); 1665 } 1666 if (retval == 0) { 1667 section = create_cache_section(RMCLOMV_VOLT_SENS, 1668 volts_r.num_volts); 1669 for (index = 0; index < volts_r.num_volts; index++) { 1670 section->entry[index].handle = 1671 volts_r.volt_status[index].handle; 1672 } 1673 add_section(&my_chain, section); 1674 } 1675 if (retval == 0) { 1676 temp_cmd.handle = DP_NULL_HANDLE; 1677 retval = rmclomv_do_cmd(DP_GET_TEMPERATURES, 1678 DP_GET_TEMPERATURES_R, RMCRESBUFLEN, 1679 (intptr_t)&temp_cmd, (intptr_t)&temp_r); 1680 } 1681 if (retval == 0) { 1682 section = create_cache_section(RMCLOMV_TEMP_SENS, 1683 temp_r.num_temps); 1684 for (index = 0; index < temp_r.num_temps; index++) { 1685 section->entry[index].handle = 1686 temp_r.temp_status[index].handle; 1687 } 1688 add_section(&my_chain, section); 1689 } 1690 if (retval == 0) { 1691 fan_cmd.handle = DP_NULL_HANDLE; 1692 retval = rmclomv_do_cmd(DP_GET_FAN_STATUS, DP_GET_FAN_STATUS_R, 1693 RMCRESBUFLEN, (intptr_t)&fan_cmd, (intptr_t)&fan_r); 1694 } 1695 if (retval == 0) { 1696 section = create_cache_section(RMCLOMV_FAN_SENS, 1697 fan_r.num_fans); 1698 for (index = 0; index < fan_r.num_fans; index++) { 1699 section->entry[index].handle = 1700 fan_r.fan_status[index].handle; 1701 } 1702 add_section(&my_chain, section); 1703 } 1704 if (retval == 0) { 1705 ampi_cmd.handle = DP_NULL_HANDLE; 1706 retval = rmclomv_do_cmd(DP_GET_CIRCUIT_BRKS, 1707 DP_GET_CIRCUIT_BRKS_R, RMCRESBUFLEN, 1708 (intptr_t)&i_cmd, (intptr_t)&i_r); 1709 } 1710 if (retval == 0) { 1711 section = create_cache_section(RMCLOMV_AMP_IND, 1712 ampi_r.num_circuit_brks); 1713 for (index = 0; index < ampi_r.num_circuit_brks; index++) { 1714 section->entry[index].handle = 1715 ampi_r.circuit_brk_status[index].handle; 1716 } 1717 add_section(&my_chain, section); 1718 } 1719 if (retval == 0) { 1720 led_cmd.handle = DP_NULL_HANDLE; 1721 retval = rmclomv_do_cmd(DP_GET_LED_STATE, DP_GET_LED_STATE_R, 1722 RMCRESBUFLEN, (intptr_t)&led_cmd, (intptr_t)&led_r); 1723 } 1724 if (retval == 0) { 1725 section = create_cache_section(RMCLOMV_LED_IND, 1726 led_r.num_leds); 1727 for (index = 0; index < led_r.num_leds; index++) { 1728 section->entry[index].handle = 1729 led_r.led_state[index].handle; 1730 } 1731 add_section(&my_chain, section); 1732 } 1733 /* 1734 * The command DP_GET_ALARM_STATE may not be valid on 1735 * some RMC versions, so we ignore the return value 1736 * and proceed 1737 */ 1738 if (retval == 0) { 1739 alarm_cmd.handle = DP_NULL_HANDLE; 1740 retval1 = rmclomv_do_cmd(DP_GET_ALARM_STATE, 1741 DP_GET_ALARM_STATE_R, RMCRESBUFLEN, 1742 (intptr_t)&alarm_cmd, (intptr_t)&alarm_r); 1743 if ((retval1 == 0) && alarm_r.num_alarms) { 1744 section = create_cache_section(RMCLOMV_ALARM_IND, 1745 alarm_r.num_alarms); 1746 for (index = 0; index < alarm_r.num_alarms; index++) { 1747 section->entry[index].handle = 1748 alarm_r.alarm_state[index].handle; 1749 } 1750 add_section(&my_chain, section); 1751 } 1752 } 1753 if (retval == 0) { 1754 psu_cmd.handle = DP_NULL_HANDLE; 1755 retval = rmclomv_do_cmd(DP_GET_PSU_STATUS, DP_GET_PSU_STATUS_R, 1756 RMCRESBUFLEN, (intptr_t)&psu_cmd, (intptr_t)&psu_r); 1757 } 1758 if (retval == 0) { 1759 /* 1760 * WARNING: 1761 * ======= 1762 * The PSUs must be probed last so that the response data 1763 * (psu_r) is available for make_psu_subsections() below. 1764 * Note that all the responses share the same data area 1765 * which is declared as a union. 1766 */ 1767 psu_section = create_cache_section(RMCLOMV_PSU_IND, 1768 psu_r.num_psus); 1769 for (index = 0; index < psu_r.num_psus; index++) { 1770 psu_section->entry[index].handle = 1771 psu_r.psu_status[index].handle; 1772 } 1773 add_section(&my_chain, psu_section); 1774 } 1775 if (retval == 0) { 1776 for (section = my_chain; 1777 section != NULL; 1778 section = section->next_section) { 1779 retval = add_names_to_section(section); 1780 if (retval != 0) { 1781 break; 1782 } 1783 } 1784 } 1785 1786 /* 1787 * now add nodes derived from PSUs 1788 */ 1789 if (retval == 0) { 1790 make_psu_subsections(psu_section, &derived_chain, &psu_r); 1791 /* 1792 * name cache sections all set, exchange new for old 1793 */ 1794 rmclomv_reset_cache(my_chain, derived_chain, &sysinfo); 1795 } else { 1796 /* 1797 * RMC is not responding, ditch any existing cache 1798 * and just leave the special SC FRU node 1799 */ 1800 rmclomv_reset_cache(my_chain, NULL, NULL); 1801 } 1802 } 1803 1804 static void 1805 set_val_unav(envmon_sensor_t *sensor) 1806 { 1807 sensor->value = ENVMON_VAL_UNAVAILABLE; 1808 sensor->lowthresholds.warning = ENVMON_VAL_UNAVAILABLE; 1809 sensor->lowthresholds.shutdown = ENVMON_VAL_UNAVAILABLE; 1810 sensor->lowthresholds.poweroff = ENVMON_VAL_UNAVAILABLE; 1811 sensor->highthresholds.warning = ENVMON_VAL_UNAVAILABLE; 1812 sensor->highthresholds.shutdown = ENVMON_VAL_UNAVAILABLE; 1813 sensor->highthresholds.poweroff = ENVMON_VAL_UNAVAILABLE; 1814 } 1815 1816 static void 1817 set_fan_unav(envmon_fan_t *fan) 1818 { 1819 fan->speed = ENVMON_VAL_UNAVAILABLE; 1820 fan->units[0] = '\0'; 1821 fan->lowthresholds.warning = ENVMON_VAL_UNAVAILABLE; 1822 fan->lowthresholds.shutdown = ENVMON_VAL_UNAVAILABLE; 1823 fan->lowthresholds.poweroff = ENVMON_VAL_UNAVAILABLE; 1824 } 1825 1826 static int 1827 do_psu_cmd(intptr_t arg, int mode, envmon_indicator_t *env_ind, 1828 dp_get_psu_status_t *rmc_psu, dp_get_psu_status_r_t *rmc_psu_r, 1829 int detector_type) 1830 { 1831 int index; 1832 uint16_t sensor_status; 1833 rmclomv_cache_section_t *section; 1834 uint16_t indicator_mask; 1835 1836 if (ddi_copyin((caddr_t)arg, (caddr_t)env_ind, 1837 sizeof (envmon_indicator_t), mode) != 0) 1838 return (EFAULT); 1839 1840 /* ensure we've got PSU handles cached */ 1841 LOCK_CACHE 1842 1843 sensor_status = ENVMON_SENSOR_OK; 1844 section = rmclomv_find_section(rmclomv_subcache, detector_type); 1845 if (env_ind->id.name[0] == '\0') { 1846 /* request for first handle */ 1847 if ((section == NULL) || (section->num_entries == 0)) 1848 env_ind->next_id.name[0] = '\0'; 1849 else 1850 env_ind->next_id = section->entry[0].handle_name; 1851 sensor_status = ENVMON_NOT_PRESENT; 1852 } else { 1853 /* ensure name is properly terminated */ 1854 env_ind->id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 1855 if ((section == NULL) || (get_sensor_by_name(section, 1856 env_ind->id.name, &index)) != 0) { 1857 env_ind->next_id.name[0] = '\0'; 1858 sensor_status = ENVMON_NOT_PRESENT; 1859 } else if (index + 1 < section->num_entries) 1860 env_ind->next_id = 1861 section->entry[index + 1].handle_name; 1862 else 1863 env_ind->next_id.name[0] = '\0'; 1864 } 1865 if (sensor_status == ENVMON_SENSOR_OK) { 1866 /* 1867 * user correctly identified a sensor, note its 1868 * handle value and request the indicator status 1869 */ 1870 rmc_psu->handle = section->entry[index].handle; 1871 indicator_mask = section->entry[index].ind_mask; 1872 } 1873 1874 RELEASE_CACHE 1875 1876 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 1877 rmclomv_do_cmd(DP_GET_PSU_STATUS, DP_GET_PSU_STATUS_R, 1878 sizeof (dp_get_psu_status_r_t), (intptr_t)rmc_psu, 1879 (intptr_t)rmc_psu_r) != 0)) { 1880 sensor_status = ENVMON_INACCESSIBLE; 1881 } 1882 if ((env_ind->sensor_status = sensor_status) == ENVMON_SENSOR_OK) { 1883 /* 1884 * copy results into buffer for user 1885 */ 1886 if ((rmc_psu_r->psu_status[0].flag & DP_PSU_PRESENCE) == 0) 1887 env_ind->sensor_status |= ENVMON_NOT_PRESENT; 1888 if (rmc_psu_r->psu_status[0].sensor_status != 1889 DP_SENSOR_DATA_AVAILABLE) 1890 env_ind->sensor_status |= ENVMON_INACCESSIBLE; 1891 env_ind->condition = 1892 (rmc_psu_r->psu_status[0].flag & indicator_mask) == 0 ? 1893 0 : 1; 1894 } 1895 1896 if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) 1897 env_ind->sensor_status = ENVMON_INACCESSIBLE; 1898 1899 if (ddi_copyout((caddr_t)env_ind, (caddr_t)arg, 1900 sizeof (envmon_indicator_t), mode) != 0) 1901 return (EFAULT); 1902 1903 return (0); 1904 } 1905 1906 /*ARGSUSED*/ 1907 static int 1908 rmclomv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 1909 int *rval_p) 1910 { 1911 int instance = getminor(dev); 1912 envmon_sysinfo_t lomv_sysinfo; 1913 union { 1914 envmon_sensor_t u_env_sensor; 1915 envmon_indicator_t u_env_ind; 1916 envmon_fan_t u_env_fan; 1917 envmon_led_info_t u_env_ledinfo; 1918 envmon_led_ctl_t u_env_ledctl; 1919 envmon_hpu_t u_env_hpu; 1920 envmon_alarm_info_t u_env_alarminfo; 1921 envmon_alarm_ctl_t u_env_alarmctl; 1922 } env_buf; 1923 #define env_sensor env_buf.u_env_sensor 1924 #define env_ind env_buf.u_env_ind 1925 #define env_fan env_buf.u_env_fan 1926 #define env_ledinfo env_buf.u_env_ledinfo 1927 #define env_ledctl env_buf.u_env_ledctl 1928 #define env_hpu env_buf.u_env_hpu 1929 #define env_alarminfo env_buf.u_env_alarminfo 1930 #define env_alarmctl env_buf.u_env_alarmctl 1931 1932 union { 1933 dp_get_volts_t u_rmc_volts; 1934 dp_get_temperatures_t u_rmc_temp; 1935 dp_get_circuit_brks_t u_rmc_ampi; 1936 dp_get_fan_status_t u_rmc_fan; 1937 dp_get_psu_status_t u_rmc_psu; 1938 dp_get_fru_status_t u_rmc_fru; 1939 dp_get_led_state_t u_rmc_led; 1940 dp_set_led_state_t u_rmc_setled; 1941 dp_get_alarm_state_t u_rmc_alarm; 1942 dp_set_alarm_state_t u_rmc_setalarm; 1943 } rmc_reqbuf; 1944 #define rmc_volts rmc_reqbuf.u_rmc_volts 1945 #define rmc_temp rmc_reqbuf.u_rmc_temp 1946 #define rmc_ampi rmc_reqbuf.u_rmc_ampi 1947 #define rmc_fan rmc_reqbuf.u_rmc_fan 1948 #define rmc_psu rmc_reqbuf.u_rmc_psu 1949 #define rmc_fru rmc_reqbuf.u_rmc_fru 1950 #define rmc_led rmc_reqbuf.u_rmc_led 1951 #define rmc_setled rmc_reqbuf.u_rmc_setled 1952 #define rmc_alarm rmc_reqbuf.u_rmc_alarm 1953 #define rmc_setalarm rmc_reqbuf.u_rmc_setalarm 1954 1955 union { 1956 dp_get_volts_r_t u_rmc_volts_r; 1957 dp_get_temperatures_r_t u_rmc_temp_r; 1958 dp_get_circuit_brks_r_t u_rmc_ampi_r; 1959 dp_get_fan_status_r_t u_rmc_fan_r; 1960 dp_get_psu_status_r_t u_rmc_psu_r; 1961 dp_get_fru_status_r_t u_rmc_fru_r; 1962 dp_get_led_state_r_t u_rmc_led_r; 1963 dp_set_led_state_r_t u_rmc_setled_r; 1964 dp_get_alarm_state_r_t u_rmc_alarm_r; 1965 dp_set_alarm_state_r_t u_rmc_setalarm_r; 1966 dp_get_sdp_version_r_t u_rmc_sdpversion_r; 1967 dp_get_serialnum_r_t u_rmc_serialnum_r; 1968 } rmc_resbuf; 1969 #define rmc_volts_r rmc_resbuf.u_rmc_volts_r 1970 #define rmc_temp_r rmc_resbuf.u_rmc_temp_r 1971 #define rmc_ampi_r rmc_resbuf.u_rmc_ampi_r 1972 #define rmc_fan_r rmc_resbuf.u_rmc_fan_r 1973 #define rmc_psu_r rmc_resbuf.u_rmc_psu_r 1974 #define rmc_fru_r rmc_resbuf.u_rmc_fru_r 1975 #define rmc_led_r rmc_resbuf.u_rmc_led_r 1976 #define rmc_setled_r rmc_resbuf.u_rmc_setled_r 1977 #define rmc_alarm_r rmc_resbuf.u_rmc_alarm_r 1978 #define rmc_setalarm_r rmc_resbuf.u_rmc_setalarm_r 1979 #define rmc_sdpver_r rmc_resbuf.u_rmc_sdpversion_r 1980 #define rmc_serialnum_r rmc_resbuf.u_rmc_serialnum_r 1981 1982 int retval = 0; 1983 int special = 0; 1984 int index; 1985 uint16_t sensor_status; 1986 rmclomv_cache_section_t *section; 1987 envmon_chassis_t chassis; 1988 1989 if (instance != 0) 1990 return (ENXIO); 1991 1992 switch (cmd) { 1993 case ENVMONIOCSYSINFO: 1994 1995 LOCK_CACHE 1996 1997 /* 1998 * A number of OK/not_OK indicators are supported by PSUs 1999 * (voltage, current, fan, temperature). So the maximum 2000 * number of such indicators relates to the maximum number 2001 * of power-supplies. 2002 */ 2003 if (rmclomv_sysinfo_valid) { 2004 lomv_sysinfo.maxVoltSens = rmclomv_sysinfo_data.maxVolt; 2005 lomv_sysinfo.maxVoltInd = 2006 RMCLOMV_MAX_VI_PER_PSU * 2007 rmclomv_sysinfo_data.maxPSU; 2008 /* 2009 * the ALOM-Solaris interface does not include 2010 * amp sensors, so we can hard code this value 2011 */ 2012 lomv_sysinfo.maxAmpSens = 0; 2013 lomv_sysinfo.maxAmpInd = 2014 rmclomv_sysinfo_data.maxCircuitBrks + 2015 (RMCLOMV_MAX_CI_PER_PSU * 2016 rmclomv_sysinfo_data.maxPSU); 2017 lomv_sysinfo.maxTempSens = rmclomv_sysinfo_data.maxTemp; 2018 lomv_sysinfo.maxTempInd = 2019 (RMCLOMV_MAX_TI_PER_PSU * 2020 rmclomv_sysinfo_data.maxPSU); 2021 lomv_sysinfo.maxFanSens = rmclomv_sysinfo_data.maxFan; 2022 lomv_sysinfo.maxFanInd = 2023 RMCLOMV_MAX_FI_PER_PSU * 2024 rmclomv_sysinfo_data.maxPSU; 2025 lomv_sysinfo.maxLED = rmclomv_sysinfo_data.maxLED; 2026 lomv_sysinfo.maxHPU = RMCLOMV_NUM_SPECIAL_FRUS + 2027 rmclomv_sysinfo_data.maxFRU; 2028 } else { 2029 bzero(&lomv_sysinfo, sizeof (lomv_sysinfo)); 2030 lomv_sysinfo.maxHPU = 1; /* just the SC node */ 2031 } 2032 2033 RELEASE_CACHE 2034 2035 if (ddi_copyout((caddr_t)&lomv_sysinfo, (caddr_t)arg, 2036 sizeof (lomv_sysinfo), mode) != 0) 2037 return (EFAULT); 2038 break; 2039 2040 case ENVMONIOCVOLTSENSOR: 2041 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor, 2042 sizeof (envmon_sensor_t), mode) != 0) 2043 return (EFAULT); 2044 2045 /* see if we've got volts handles cached */ 2046 LOCK_CACHE 2047 sensor_status = ENVMON_SENSOR_OK; 2048 2049 if ((rmclomv_cache_valid == B_FALSE) || 2050 ((section = rmclomv_find_section(rmclomv_cache, 2051 RMCLOMV_VOLT_SENS)) == NULL)) { 2052 env_sensor.next_id.name[0] = '\0'; 2053 sensor_status = ENVMON_NOT_PRESENT; 2054 } else if (env_sensor.id.name[0] == '\0') { 2055 /* request for first handle */ 2056 if (section->num_entries == 0) 2057 env_sensor.next_id.name[0] = '\0'; 2058 else 2059 env_sensor.next_id = 2060 section->entry[0].handle_name; 2061 sensor_status = ENVMON_NOT_PRESENT; 2062 } else { 2063 /* ensure name is properly terminated */ 2064 env_sensor.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2065 if (get_sensor_by_name(section, env_sensor.id.name, 2066 &index) != 0) { 2067 env_sensor.next_id.name[0] = '\0'; 2068 sensor_status = ENVMON_NOT_PRESENT; 2069 } else if (index + 1 < section->num_entries) 2070 env_sensor.next_id = 2071 section->entry[index + 1].handle_name; 2072 else 2073 env_sensor.next_id.name[0] = '\0'; 2074 } 2075 if (sensor_status == ENVMON_SENSOR_OK) { 2076 /* 2077 * user correctly identified a sensor, note its 2078 * handle value and request the sensor value 2079 */ 2080 rmc_volts.handle = section->entry[index].handle; 2081 } 2082 RELEASE_CACHE 2083 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 2084 rmclomv_do_cmd(DP_GET_VOLTS, DP_GET_VOLTS_R, 2085 sizeof (rmc_volts_r), (intptr_t)&rmc_volts, 2086 (intptr_t)&rmc_volts_r) != 0)) { 2087 sensor_status = ENVMON_INACCESSIBLE; 2088 } 2089 if ((sensor_status == ENVMON_SENSOR_OK) && 2090 (rmc_volts_r.volt_status[0].sensor_status == 2091 DP_SENSOR_NOT_PRESENT)) { 2092 sensor_status = ENVMON_NOT_PRESENT; 2093 } 2094 if ((env_sensor.sensor_status = sensor_status) == 2095 ENVMON_SENSOR_OK) { 2096 /* 2097 * copy results into buffer for user 2098 */ 2099 if (rmc_volts_r.volt_status[0].sensor_status != 2100 DP_SENSOR_DATA_AVAILABLE) 2101 env_sensor.sensor_status = ENVMON_INACCESSIBLE; 2102 env_sensor.value = 2103 rmc_volts_r.volt_status[0].reading; 2104 env_sensor.lowthresholds.warning = 2105 rmc_volts_r.volt_status[0].low_warning; 2106 env_sensor.lowthresholds.shutdown = 2107 rmc_volts_r.volt_status[0].low_soft_shutdown; 2108 env_sensor.lowthresholds.poweroff = 2109 rmc_volts_r.volt_status[0].low_hard_shutdown; 2110 env_sensor.highthresholds.warning = 2111 rmc_volts_r.volt_status[0].high_warning; 2112 env_sensor.highthresholds.shutdown = 2113 rmc_volts_r.volt_status[0].high_soft_shutdown; 2114 env_sensor.highthresholds.poweroff = 2115 rmc_volts_r.volt_status[0].high_hard_shutdown; 2116 } 2117 if (env_sensor.sensor_status != ENVMON_SENSOR_OK || 2118 rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) 2119 set_val_unav(&env_sensor); 2120 2121 if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg, 2122 sizeof (envmon_sensor_t), mode) != 0) 2123 return (EFAULT); 2124 break; 2125 2126 case ENVMONIOCVOLTIND: 2127 return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r, 2128 RMCLOMV_VOLT_IND)); 2129 2130 case ENVMONIOCTEMPIND: 2131 return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r, 2132 RMCLOMV_TEMP_IND)); 2133 2134 case ENVMONIOCFANIND: 2135 return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, &rmc_psu_r, 2136 RMCLOMV_FAN_IND)); 2137 2138 case ENVMONIOCAMPSENSOR: 2139 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor, 2140 sizeof (envmon_sensor_t), mode) != 0) 2141 return (EFAULT); 2142 2143 env_sensor.sensor_status = ENVMON_NOT_PRESENT; 2144 env_sensor.next_id.name[0] = '\0'; 2145 2146 if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg, 2147 sizeof (envmon_sensor_t), mode) != 0) 2148 return (EFAULT); 2149 break; 2150 2151 case ENVMONIOCTEMPSENSOR: 2152 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_sensor, 2153 sizeof (envmon_sensor_t), mode) != 0) 2154 return (EFAULT); 2155 2156 /* see if we've got temperature handles cached */ 2157 LOCK_CACHE 2158 sensor_status = ENVMON_SENSOR_OK; 2159 2160 if ((rmclomv_cache_valid == B_FALSE) || 2161 ((section = rmclomv_find_section(rmclomv_cache, 2162 RMCLOMV_TEMP_SENS)) == NULL)) { 2163 env_sensor.next_id.name[0] = '\0'; 2164 sensor_status = ENVMON_NOT_PRESENT; 2165 } else if (env_sensor.id.name[0] == '\0') { 2166 /* request for first handle */ 2167 if (section->num_entries == 0) 2168 env_sensor.next_id.name[0] = '\0'; 2169 else 2170 env_sensor.next_id = 2171 section->entry[0].handle_name; 2172 sensor_status = ENVMON_NOT_PRESENT; 2173 } else { 2174 /* ensure name is properly terminated */ 2175 env_sensor.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2176 if (get_sensor_by_name(section, env_sensor.id.name, 2177 &index) != 0) { 2178 env_sensor.next_id.name[0] = '\0'; 2179 sensor_status = ENVMON_NOT_PRESENT; 2180 } else if (index + 1 < section->num_entries) 2181 env_sensor.next_id = 2182 section->entry[index + 1].handle_name; 2183 else 2184 env_sensor.next_id.name[0] = '\0'; 2185 } 2186 if (sensor_status == ENVMON_SENSOR_OK) { 2187 /* 2188 * user correctly identified a sensor, note its 2189 * handle value and request the sensor value 2190 */ 2191 rmc_temp.handle = section->entry[index].handle; 2192 } 2193 RELEASE_CACHE 2194 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 2195 rmclomv_do_cmd(DP_GET_TEMPERATURES, DP_GET_TEMPERATURES_R, 2196 sizeof (rmc_temp_r), (intptr_t)&rmc_temp, 2197 (intptr_t)&rmc_temp_r) != 0)) { 2198 sensor_status = ENVMON_INACCESSIBLE; 2199 } 2200 if ((sensor_status == ENVMON_SENSOR_OK) && 2201 (rmc_temp_r.temp_status[0].sensor_status == 2202 DP_SENSOR_NOT_PRESENT)) { 2203 sensor_status = ENVMON_NOT_PRESENT; 2204 } 2205 if ((env_sensor.sensor_status = sensor_status) == 2206 ENVMON_SENSOR_OK) { 2207 /* 2208 * copy results into buffer for user 2209 */ 2210 if (rmc_temp_r.temp_status[0].sensor_status != 2211 DP_SENSOR_DATA_AVAILABLE) 2212 env_sensor.sensor_status = ENVMON_INACCESSIBLE; 2213 env_sensor.value = 2214 rmc_temp_r.temp_status[0].value; 2215 env_sensor.lowthresholds.warning = 2216 rmc_temp_r.temp_status[0].low_warning; 2217 env_sensor.lowthresholds.shutdown = 2218 rmc_temp_r.temp_status[0].low_soft_shutdown; 2219 env_sensor.lowthresholds.poweroff = 2220 rmc_temp_r.temp_status[0].low_hard_shutdown; 2221 env_sensor.highthresholds.warning = 2222 rmc_temp_r.temp_status[0].high_warning; 2223 env_sensor.highthresholds.shutdown = 2224 rmc_temp_r.temp_status[0].high_soft_shutdown; 2225 env_sensor.highthresholds.poweroff = 2226 rmc_temp_r.temp_status[0].high_hard_shutdown; 2227 } 2228 if (env_sensor.sensor_status != ENVMON_SENSOR_OK || 2229 rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) 2230 set_val_unav(&env_sensor); 2231 2232 if (ddi_copyout((caddr_t)&env_sensor, (caddr_t)arg, 2233 sizeof (envmon_sensor_t), mode) != 0) 2234 return (EFAULT); 2235 break; 2236 2237 2238 case ENVMONIOCFAN: 2239 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_fan, 2240 sizeof (envmon_fan_t), mode) != 0) 2241 return (EFAULT); 2242 2243 /* see if we've got fan handles cached */ 2244 LOCK_CACHE 2245 sensor_status = ENVMON_SENSOR_OK; 2246 2247 if ((rmclomv_cache_valid == B_FALSE) || 2248 ((section = rmclomv_find_section(rmclomv_cache, 2249 RMCLOMV_FAN_SENS)) == NULL)) { 2250 env_fan.next_id.name[0] = '\0'; 2251 sensor_status = ENVMON_NOT_PRESENT; 2252 } else if (env_fan.id.name[0] == '\0') { 2253 /* request for first handle */ 2254 if (section->num_entries == 0) 2255 env_fan.next_id.name[0] = '\0'; 2256 else 2257 env_fan.next_id = 2258 section->entry[0].handle_name; 2259 sensor_status = ENVMON_NOT_PRESENT; 2260 } else { 2261 /* ensure name is properly terminated */ 2262 env_fan.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2263 if (get_sensor_by_name(section, env_fan.id.name, 2264 &index) != 0) { 2265 env_fan.next_id.name[0] = '\0'; 2266 sensor_status = ENVMON_NOT_PRESENT; 2267 } else if (index + 1 < section->num_entries) 2268 env_fan.next_id = 2269 section->entry[index + 1].handle_name; 2270 else 2271 env_fan.next_id.name[0] = '\0'; 2272 } 2273 if (sensor_status == ENVMON_SENSOR_OK) { 2274 /* 2275 * user correctly identified a sensor, note its 2276 * handle value and request the sensor value 2277 */ 2278 rmc_fan.handle = section->entry[index].handle; 2279 } 2280 RELEASE_CACHE 2281 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 2282 rmclomv_do_cmd(DP_GET_FAN_STATUS, DP_GET_FAN_STATUS_R, 2283 sizeof (rmc_fan_r), (intptr_t)&rmc_fan, 2284 (intptr_t)&rmc_fan_r) != 0)) { 2285 sensor_status = ENVMON_INACCESSIBLE; 2286 } 2287 if ((sensor_status == ENVMON_SENSOR_OK) && 2288 (rmc_fan_r.fan_status[0].sensor_status == 2289 DP_SENSOR_NOT_PRESENT)) { 2290 sensor_status = ENVMON_NOT_PRESENT; 2291 } 2292 if ((env_fan.sensor_status = sensor_status) == 2293 ENVMON_SENSOR_OK) { 2294 if ((rmc_fan_r.fan_status[0].flag & 2295 DP_FAN_PRESENCE) == 0) 2296 env_fan.sensor_status = ENVMON_NOT_PRESENT; 2297 if (rmc_fan_r.fan_status[0].sensor_status != 2298 DP_SENSOR_DATA_AVAILABLE) 2299 env_fan.sensor_status |= ENVMON_INACCESSIBLE; 2300 if (env_fan.sensor_status == ENVMON_SENSOR_OK) { 2301 /* 2302 * copy results into buffer for user 2303 */ 2304 env_fan.speed = 2305 rmc_fan_r.fan_status[0].speed; 2306 env_fan.lowthresholds.warning = 2307 rmc_fan_r.fan_status[0].minspeed; 2308 env_fan.lowthresholds.shutdown = 2309 ENVMON_VAL_UNAVAILABLE; 2310 env_fan.lowthresholds.poweroff = 2311 ENVMON_VAL_UNAVAILABLE; 2312 if ((rmc_fan_r.fan_status[0].flag & 2313 DP_FAN_SPEED_VAL_UNIT) == 0) 2314 bcopy(str_rpm, env_fan.units, 2315 sizeof (str_rpm)); 2316 else 2317 bcopy(str_percent, env_fan.units, 2318 sizeof (str_percent)); 2319 } 2320 } 2321 if (env_fan.sensor_status != ENVMON_SENSOR_OK || 2322 rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) 2323 set_fan_unav(&env_fan); 2324 2325 if (ddi_copyout((caddr_t)&env_fan, (caddr_t)arg, 2326 sizeof (envmon_fan_t), mode) != 0) 2327 return (EFAULT); 2328 break; 2329 2330 case ENVMONIOCAMPIND: 2331 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ind, 2332 sizeof (envmon_indicator_t), mode) != 0) 2333 return (EFAULT); 2334 2335 /* see if we've got amp indicator handles cached */ 2336 LOCK_CACHE 2337 sensor_status = ENVMON_SENSOR_OK; 2338 2339 if ((rmclomv_cache_valid == B_FALSE) || 2340 ((section = rmclomv_find_section(rmclomv_cache, 2341 RMCLOMV_AMP_IND)) == NULL)) { 2342 RELEASE_CACHE 2343 return (do_psu_cmd(arg, mode, &env_ind, &rmc_psu, 2344 &rmc_psu_r, RMCLOMV_AMP_IND)); 2345 } else if (env_ind.id.name[0] == '\0') { 2346 /* request for first handle */ 2347 if (section->num_entries == 0) { 2348 RELEASE_CACHE 2349 return (do_psu_cmd(arg, mode, &env_ind, 2350 &rmc_psu, &rmc_psu_r, RMCLOMV_AMP_IND)); 2351 } 2352 env_ind.next_id = section->entry[0].handle_name; 2353 sensor_status = ENVMON_NOT_PRESENT; 2354 } else { 2355 /* ensure name is properly terminated */ 2356 env_ind.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2357 if (get_sensor_by_name(section, env_ind.id.name, 2358 &index) != 0) { 2359 RELEASE_CACHE 2360 return (do_psu_cmd(arg, mode, &env_ind, 2361 &rmc_psu, &rmc_psu_r, RMCLOMV_AMP_IND)); 2362 } 2363 if (index + 1 < section->num_entries) { 2364 env_ind.next_id = 2365 section->entry[index + 1].handle_name; 2366 } else { 2367 rmclomv_cache_section_t *sub_section = 2368 rmclomv_find_section(rmclomv_subcache, 2369 RMCLOMV_AMP_IND); 2370 if ((sub_section == NULL) || 2371 (sub_section->num_entries == 0)) 2372 env_ind.next_id.name[0] = '\0'; 2373 else 2374 env_ind.next_id = 2375 sub_section->entry[0].handle_name; 2376 } 2377 } 2378 if (sensor_status == ENVMON_SENSOR_OK) { 2379 /* 2380 * user correctly identified an indicator, note its 2381 * handle value and request the indicator status 2382 */ 2383 rmc_ampi.handle = section->entry[index].handle; 2384 } 2385 RELEASE_CACHE 2386 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 2387 rmclomv_do_cmd(DP_GET_CIRCUIT_BRKS, DP_GET_CIRCUIT_BRKS_R, 2388 sizeof (rmc_ampi_r), (intptr_t)&rmc_ampi, 2389 (intptr_t)&rmc_ampi_r) != 0)) { 2390 sensor_status = ENVMON_INACCESSIBLE; 2391 } 2392 if ((sensor_status == ENVMON_SENSOR_OK) && 2393 (rmc_ampi_r.circuit_brk_status[0].sensor_status == 2394 DP_SENSOR_NOT_PRESENT)) { 2395 sensor_status = ENVMON_NOT_PRESENT; 2396 } 2397 if ((env_ind.sensor_status = sensor_status) == 2398 ENVMON_SENSOR_OK) { 2399 /* 2400 * copy results into buffer for user 2401 */ 2402 if (rmc_ampi_r.circuit_brk_status[0].sensor_status != 2403 DP_SENSOR_DATA_AVAILABLE) 2404 env_ind.sensor_status = ENVMON_INACCESSIBLE; 2405 env_ind.condition = 2406 rmc_ampi_r.circuit_brk_status[0].status; 2407 } 2408 2409 /* 2410 * If rmclomv_rmc_error is set there is no way 2411 * that we read information from RSC. Just copy 2412 * out an inaccessible evironmental. 2413 */ 2414 if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) { 2415 env_ind.sensor_status = ENVMON_INACCESSIBLE; 2416 env_ind.condition = ENVMON_INACCESSIBLE; 2417 } 2418 2419 if (ddi_copyout((caddr_t)&env_ind, (caddr_t)arg, 2420 sizeof (envmon_indicator_t), mode) != 0) 2421 return (EFAULT); 2422 break; 2423 2424 case ENVMONIOCHPU: 2425 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_hpu, 2426 sizeof (envmon_hpu_t), mode) != 0) 2427 return (EFAULT); 2428 2429 /* see if we've got hpu handles cached */ 2430 LOCK_CACHE 2431 2432 if ((rmclomv_cache_valid == B_FALSE) || 2433 ((section = rmclomv_find_section(rmclomv_cache, 2434 RMCLOMV_HPU_IND)) == NULL)) { 2435 RELEASE_CACHE 2436 return (EAGAIN); 2437 } 2438 2439 /* 2440 * At this point the cache is locked and section points to 2441 * the section relating to hpus. 2442 */ 2443 sensor_status = ENVMON_SENSOR_OK; 2444 if (env_hpu.id.name[0] == '\0') { 2445 /* request for first handle */ 2446 if (section->num_entries == 0) 2447 env_hpu.next_id.name[0] = '\0'; 2448 else 2449 env_hpu.next_id = 2450 section->entry[0].handle_name; 2451 sensor_status = ENVMON_NOT_PRESENT; 2452 } else { 2453 /* ensure name is properly terminated */ 2454 env_hpu.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2455 if (get_sensor_by_name(section, env_hpu.id.name, 2456 &index) != 0) { 2457 env_hpu.next_id.name[0] = '\0'; 2458 sensor_status = ENVMON_NOT_PRESENT; 2459 } else if (index + 1 < section->num_entries) 2460 env_hpu.next_id = 2461 section->entry[index + 1].handle_name; 2462 else 2463 env_hpu.next_id.name[0] = '\0'; 2464 } 2465 if (sensor_status == ENVMON_SENSOR_OK) { 2466 /* 2467 * user correctly identified an hpu, note its 2468 * handle value and request the hpu status 2469 */ 2470 rmc_fru.handle = section->entry[index].handle; 2471 special = section->entry[index].ind_mask; 2472 } 2473 RELEASE_CACHE 2474 if ((env_hpu.sensor_status = sensor_status) == 2475 ENVMON_SENSOR_OK) { 2476 env_hpu.fru_status = ENVMON_FRU_PRESENT; 2477 2478 if (special != 0) { 2479 /* this is the pseudo SC node */ 2480 mutex_enter(&rmclomv_state_lock); 2481 switch (rmclomv_rmc_state) { 2482 case RMCLOMV_RMCSTATE_OK: 2483 break; 2484 case RMCLOMV_RMCSTATE_FAILED: 2485 env_hpu.fru_status = ENVMON_FRU_FAULT; 2486 break; 2487 case RMCLOMV_RMCSTATE_DOWNLOAD: 2488 env_hpu.fru_status = 2489 ENVMON_FRU_DOWNLOAD; 2490 break; 2491 default: 2492 env_hpu.sensor_status = 2493 ENVMON_INACCESSIBLE; 2494 break; 2495 } 2496 mutex_exit(&rmclomv_state_lock); 2497 } else if (rmclomv_rmc_error || 2498 rmclomv_do_cmd(DP_GET_FRU_STATUS, 2499 DP_GET_FRU_STATUS_R, sizeof (rmc_fru_r), 2500 (intptr_t)&rmc_fru, (intptr_t)&rmc_fru_r) != 0) { 2501 env_hpu.sensor_status = ENVMON_INACCESSIBLE; 2502 } else { 2503 /* 2504 * copy results into buffer for user 2505 */ 2506 if (rmc_fru_r.fru_status[0].presence == 0) { 2507 env_hpu.sensor_status = 2508 ENVMON_NOT_PRESENT; 2509 env_hpu.fru_status = 2510 ENVMON_FRU_NOT_PRESENT; 2511 } else if (rmc_fru_r.fru_status[0].sensor_status 2512 != DP_SENSOR_DATA_AVAILABLE) { 2513 env_hpu.sensor_status = 2514 ENVMON_INACCESSIBLE; 2515 } else { 2516 uint8_t status = 2517 rmc_fru_r.fru_status[0].status; 2518 if (status == DP_FRU_STATUS_UNKNOWN) { 2519 env_hpu.sensor_status = 2520 ENVMON_INACCESSIBLE; 2521 } else if (status != DP_FRU_STATUS_OK) { 2522 env_hpu.fru_status = 2523 ENVMON_FRU_FAULT; 2524 } 2525 } 2526 } 2527 } 2528 2529 /* 2530 * If rmclomv_rmc_error is set there is no way 2531 * that we read information from RSC. Just copy 2532 * out an inaccessible environmental. 2533 */ 2534 if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) { 2535 env_hpu.sensor_status = ENVMON_INACCESSIBLE; 2536 env_hpu.fru_status = ENVMON_INACCESSIBLE; 2537 } 2538 2539 if (ddi_copyout((caddr_t)&env_hpu, (caddr_t)arg, 2540 sizeof (envmon_hpu_t), mode) != 0) 2541 return (EFAULT); 2542 break; 2543 2544 case ENVMONIOCGETLED: 2545 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ledinfo, 2546 sizeof (envmon_led_info_t), mode) != 0) 2547 return (EFAULT); 2548 2549 /* see if we've got LED handles cached */ 2550 LOCK_CACHE 2551 sensor_status = ENVMON_SENSOR_OK; 2552 2553 if ((rmclomv_cache_valid == B_FALSE) || 2554 ((section = rmclomv_find_section(rmclomv_cache, 2555 RMCLOMV_LED_IND)) == NULL)) { 2556 env_ledinfo.next_id.name[0] = '\0'; 2557 sensor_status = ENVMON_NOT_PRESENT; 2558 } else if (env_ledinfo.id.name[0] == '\0') { 2559 /* request for first handle */ 2560 if (section->num_entries == 0) 2561 env_ledinfo.next_id.name[0] = '\0'; 2562 else 2563 env_ledinfo.next_id = 2564 section->entry[0].handle_name; 2565 sensor_status = ENVMON_NOT_PRESENT; 2566 } else { 2567 /* ensure name is properly terminated */ 2568 env_ledinfo.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2569 if (get_sensor_by_name(section, env_ledinfo.id.name, 2570 &index) != 0) { 2571 env_ledinfo.next_id.name[0] = '\0'; 2572 sensor_status = ENVMON_NOT_PRESENT; 2573 } else if (index + 1 < section->num_entries) 2574 env_ledinfo.next_id = 2575 section->entry[index + 1].handle_name; 2576 else 2577 env_ledinfo.next_id.name[0] = '\0'; 2578 } 2579 if (sensor_status == ENVMON_SENSOR_OK) { 2580 /* 2581 * user correctly identified a LED, note its 2582 * handle value and request the LED status 2583 */ 2584 rmc_led.handle = section->entry[index].handle; 2585 } 2586 RELEASE_CACHE 2587 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 2588 rmclomv_do_cmd(DP_GET_LED_STATE, DP_GET_LED_STATE_R, 2589 sizeof (rmc_led_r), (intptr_t)&rmc_led, 2590 (intptr_t)&rmc_led_r) != 0)) { 2591 sensor_status = ENVMON_INACCESSIBLE; 2592 } 2593 if ((sensor_status == ENVMON_SENSOR_OK) && 2594 (rmc_led_r.led_state[0].sensor_status == 2595 DP_SENSOR_NOT_PRESENT)) { 2596 sensor_status = ENVMON_NOT_PRESENT; 2597 } 2598 if ((env_ledinfo.sensor_status = sensor_status) == 2599 ENVMON_SENSOR_OK) { 2600 /* 2601 * copy results into buffer for user 2602 * start with some defaults then override 2603 */ 2604 env_ledinfo.sensor_status = ENVMON_SENSOR_OK; 2605 env_ledinfo.led_state = ENVMON_LED_OFF; 2606 env_ledinfo.led_color = ENVMON_LED_CLR_NONE; 2607 2608 if (rmc_led_r.led_state[0].sensor_status != 2609 DP_SENSOR_DATA_AVAILABLE) 2610 env_ledinfo.sensor_status = ENVMON_INACCESSIBLE; 2611 else { 2612 dp_led_state_t ledState; 2613 ledState = rmc_led_r.led_state[0]; 2614 env_ledinfo.led_color = (int8_t)ledState.colour; 2615 2616 switch (ledState.state) { 2617 case (rsci8)DP_LED_OFF: 2618 break; 2619 case (rsci8)DP_LED_ON: 2620 env_ledinfo.led_state = ENVMON_LED_ON; 2621 break; 2622 case (rsci8)DP_LED_BLINKING: 2623 env_ledinfo.led_state = 2624 ENVMON_LED_BLINKING; 2625 break; 2626 case (rsci8)DP_LED_FLASHING: 2627 env_ledinfo.led_state = 2628 ENVMON_LED_FLASHING; 2629 break; 2630 default: 2631 break; 2632 } 2633 } 2634 } 2635 2636 /* 2637 * If rmclomv_rmc_error is set there is no way 2638 * that we read information from RSC. Just copy 2639 * out an inaccessible environmental. 2640 */ 2641 if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) { 2642 env_ledinfo.sensor_status = ENVMON_INACCESSIBLE; 2643 env_ledinfo.led_state = ENVMON_INACCESSIBLE; 2644 } 2645 2646 if (ddi_copyout((caddr_t)&env_ledinfo, (caddr_t)arg, 2647 sizeof (envmon_led_info_t), mode) != 0) 2648 return (EFAULT); 2649 break; 2650 2651 case ENVMONIOCSETLED: 2652 if ((mode & FWRITE) == 0) 2653 return (EACCES); 2654 if (drv_priv(cred_p) != 0) 2655 return (EPERM); 2656 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_ledctl, 2657 sizeof (envmon_led_ctl_t), mode) != 0) 2658 return (EFAULT); 2659 if (env_ledctl.led_state < RMCLOMV_MIN_LED_STATE || 2660 env_ledctl.led_state > RMCLOMV_MAX_LED_STATE) 2661 return (EINVAL); 2662 /* 2663 * Ensure name is properly terminated. 2664 */ 2665 env_ledctl.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2666 2667 /* see if we've got LED handles cached */ 2668 LOCK_CACHE 2669 2670 if ((rmclomv_cache_valid == B_FALSE) || 2671 ((section = rmclomv_find_section(rmclomv_cache, 2672 RMCLOMV_LED_IND)) == NULL) || 2673 (get_sensor_by_name(section, env_ledctl.id.name, 2674 &index) != 0)) { 2675 RELEASE_CACHE 2676 return (EINVAL); /* no such LED */ 2677 } 2678 /* 2679 * user correctly identified a LED, note its handle value 2680 */ 2681 rmc_setled.handle = section->entry[index].handle; 2682 RELEASE_CACHE 2683 switch (env_ledctl.led_state) { 2684 case ENVMON_LED_ON: 2685 rmc_setled.state = DP_LED_ON; 2686 break; 2687 case ENVMON_LED_BLINKING: 2688 rmc_setled.state = DP_LED_BLINKING; 2689 break; 2690 case ENVMON_LED_FLASHING: 2691 rmc_setled.state = DP_LED_FLASHING; 2692 break; 2693 default: 2694 rmc_setled.state = DP_LED_OFF; 2695 break; 2696 } 2697 retval = rmclomv_do_cmd(DP_SET_LED_STATE, DP_SET_LED_STATE_R, 2698 sizeof (rmc_setled_r), (intptr_t)&rmc_setled, 2699 (intptr_t)&rmc_setled_r); 2700 2701 if (retval != 0) { 2702 break; 2703 } 2704 2705 if (rmc_setled_r.status != 0) { 2706 cmn_err(CE_WARN, "ENVMONIOCSETLED: \"%s\" status: 0x%x", 2707 env_ledctl.id.name, rmc_setled_r.status); 2708 return (EIO); 2709 } 2710 break; 2711 2712 case ENVMONIOCGETKEYSW: 2713 { 2714 enum rmc_keyswitch_pos rmc_pos = real_key_position; 2715 envmon_keysw_pos_t envmon_pos; 2716 2717 /* 2718 * Yes, I know this is ugly, but the V210 has no keyswitch, 2719 * even though the ALOM returns a value for it 2720 */ 2721 if (strcmp(platform, "SUNW,Sun-Fire-V210") == 0) { 2722 return (ENOTSUP); 2723 } 2724 2725 switch (rmc_pos) { 2726 2727 case RMC_KEYSWITCH_POS_NORMAL: 2728 envmon_pos = ENVMON_KEYSW_POS_NORMAL; 2729 break; 2730 case RMC_KEYSWITCH_POS_DIAG: 2731 envmon_pos = ENVMON_KEYSW_POS_DIAG; 2732 break; 2733 case RMC_KEYSWITCH_POS_LOCKED: 2734 envmon_pos = ENVMON_KEYSW_POS_LOCKED; 2735 break; 2736 case RMC_KEYSWITCH_POS_OFF: 2737 envmon_pos = ENVMON_KEYSW_POS_OFF; 2738 break; 2739 default: 2740 envmon_pos = ENVMON_KEYSW_POS_UNKNOWN; 2741 break; 2742 } 2743 2744 if (ddi_copyout((caddr_t)&envmon_pos, (caddr_t)arg, 2745 sizeof (envmon_pos), mode) != 0) 2746 return (EFAULT); 2747 break; 2748 } 2749 2750 case ENVMONIOCGETALARM: 2751 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_alarminfo, 2752 sizeof (envmon_alarm_info_t), mode) != 0) 2753 return (EFAULT); 2754 2755 /* see if we've got ALARM handles cached */ 2756 LOCK_CACHE 2757 sensor_status = ENVMON_SENSOR_OK; 2758 2759 if ((rmclomv_cache_valid == B_FALSE) || 2760 ((section = rmclomv_find_section(rmclomv_cache, 2761 RMCLOMV_ALARM_IND)) == NULL)) { 2762 env_alarminfo.next_id.name[0] = '\0'; 2763 sensor_status = ENVMON_NOT_PRESENT; 2764 } else if (env_alarminfo.id.name[0] == '\0') { 2765 /* request for first handle */ 2766 if (section->num_entries == 0) 2767 env_alarminfo.next_id.name[0] = '\0'; 2768 else 2769 env_alarminfo.next_id = 2770 section->entry[0].handle_name; 2771 sensor_status = ENVMON_NOT_PRESENT; 2772 } else { 2773 /* ensure name is properly terminated */ 2774 env_alarminfo.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2775 if (get_sensor_by_name(section, env_alarminfo.id.name, 2776 &index) != 0) { 2777 env_alarminfo.next_id.name[0] = '\0'; 2778 sensor_status = ENVMON_NOT_PRESENT; 2779 } else if (index + 1 < section->num_entries) 2780 env_alarminfo.next_id = 2781 section->entry[index + 1].handle_name; 2782 else 2783 env_alarminfo.next_id.name[0] = '\0'; 2784 } 2785 if (sensor_status == ENVMON_SENSOR_OK) { 2786 /* 2787 * user correctly identified a ALARM, note its 2788 * handle value and request the ALARM status 2789 */ 2790 rmc_alarm.handle = section->entry[index].handle; 2791 } 2792 RELEASE_CACHE 2793 if ((sensor_status == ENVMON_SENSOR_OK) && 2794 (rmclomv_rmc_error || 2795 rmclomv_do_cmd(DP_GET_ALARM_STATE, DP_GET_ALARM_STATE_R, 2796 sizeof (rmc_alarm_r), (intptr_t)&rmc_alarm, 2797 (intptr_t)&rmc_alarm_r) != 0)) { 2798 sensor_status = ENVMON_INACCESSIBLE; 2799 } 2800 if ((env_alarminfo.sensor_status = sensor_status) == 2801 ENVMON_SENSOR_OK) { 2802 /* 2803 * copy results into buffer for user 2804 * start with some defaults then override 2805 */ 2806 env_alarminfo.sensor_status = ENVMON_SENSOR_OK; 2807 env_alarminfo.alarm_state = ENVMON_ALARM_OFF; 2808 2809 if (rmc_alarm_r.alarm_state[0].sensor_status != 2810 DP_SENSOR_DATA_AVAILABLE) 2811 env_alarminfo.sensor_status = 2812 ENVMON_INACCESSIBLE; 2813 else { 2814 dp_alarm_state_t alarmState; 2815 alarmState = rmc_alarm_r.alarm_state[0]; 2816 2817 switch (alarmState.state) { 2818 case DP_ALARM_OFF: 2819 break; 2820 case DP_ALARM_ON: 2821 env_alarminfo.alarm_state = 2822 ENVMON_ALARM_ON; 2823 break; 2824 default: 2825 break; 2826 } 2827 } 2828 } 2829 2830 /* 2831 * If rmclomv_rmc_error is set there is no way 2832 * that we read information from RSC. Just copy 2833 * out an inaccessible environmental. 2834 */ 2835 if (rmclomv_rmc_error != RMCLOMV_RMCERROR_NONE) { 2836 env_alarminfo.sensor_status = ENVMON_INACCESSIBLE; 2837 env_alarminfo.alarm_state = ENVMON_INACCESSIBLE; 2838 } 2839 2840 if (ddi_copyout((caddr_t)&env_alarminfo, (caddr_t)arg, 2841 sizeof (envmon_alarm_info_t), mode) != 0) 2842 return (EFAULT); 2843 break; 2844 2845 case ENVMONIOCSETALARM: 2846 if ((mode & FWRITE) == 0) 2847 return (EACCES); 2848 if (drv_priv(cred_p) != 0) 2849 return (EPERM); 2850 if (ddi_copyin((caddr_t)arg, (caddr_t)&env_alarmctl, 2851 sizeof (envmon_alarm_ctl_t), mode) != 0) 2852 return (EFAULT); 2853 if (env_alarmctl.alarm_state < RMCLOMV_MIN_ALARM_STATE || 2854 env_alarmctl.alarm_state > RMCLOMV_MAX_ALARM_STATE) 2855 return (EINVAL); 2856 /* 2857 * Ensure name is properly terminated. 2858 */ 2859 env_alarmctl.id.name[ENVMON_MAXNAMELEN - 1] = '\0'; 2860 2861 /* see if we've got ALARM handles cached */ 2862 LOCK_CACHE 2863 2864 if ((rmclomv_cache_valid == B_FALSE) || 2865 ((section = rmclomv_find_section(rmclomv_cache, 2866 RMCLOMV_ALARM_IND)) == NULL) || 2867 (get_sensor_by_name(section, env_alarmctl.id.name, 2868 &index) != 0)) { 2869 RELEASE_CACHE 2870 return (EINVAL); /* no such ALARM */ 2871 } 2872 /* 2873 * user correctly identified a ALARM, note its handle value 2874 */ 2875 rmc_setalarm.handle = section->entry[index].handle; 2876 RELEASE_CACHE 2877 rmc_setalarm.state = (rsci8)env_alarmctl.alarm_state; 2878 retval = rmclomv_do_cmd(DP_SET_ALARM_STATE, 2879 DP_SET_ALARM_STATE_R, 2880 sizeof (rmc_setalarm_r), 2881 (intptr_t)&rmc_setalarm, 2882 (intptr_t)&rmc_setalarm_r); 2883 2884 if (retval != 0) { 2885 break; 2886 } 2887 2888 if (rmc_setalarm_r.status != 0) { 2889 cmn_err(CE_WARN, "ENVMONIOCSETALARM: \"%s\" status: " 2890 "0x%x", env_alarmctl.id.name, 2891 rmc_setalarm_r.status); 2892 return (EIO); 2893 } 2894 break; 2895 2896 case ENVMONIOCCHASSISSERIALNUM: 2897 retval = rmclomv_do_cmd(DP_GET_SDP_VERSION, 2898 DP_GET_SDP_VERSION_R, sizeof (rmc_sdpver_r), 2899 (intptr_t)NULL, (intptr_t)&rmc_sdpver_r); 2900 2901 if (retval != 0) { 2902 cmn_err(CE_WARN, "DP_GET_SDP_VERSION failed, ret=%d\n", 2903 retval); 2904 break; 2905 } else if (rmc_sdpver_r.version < SDP_RESPONDS_TO_ALL_CMDS) { 2906 retval = ENOTSUP; 2907 break; 2908 } 2909 retval = rmclomv_do_cmd(DP_GET_CHASSIS_SERIALNUM, 2910 DP_GET_CHASSIS_SERIALNUM_R, sizeof (rmc_serialnum_r), 2911 (intptr_t)NULL, (intptr_t)&rmc_serialnum_r); 2912 2913 if (retval != 0) { 2914 break; 2915 } 2916 bcopy(rmc_serialnum_r.chassis_serial_number, 2917 chassis.serial_number, 2918 sizeof (rmc_serialnum_r.chassis_serial_number)); 2919 2920 if (ddi_copyout((caddr_t)&chassis, (caddr_t)arg, 2921 sizeof (chassis), mode) != 0) { 2922 return (EFAULT); 2923 } 2924 sensor_status = ENVMON_SENSOR_OK; 2925 break; 2926 2927 default: 2928 retval = ENOTSUP; 2929 break; 2930 } 2931 2932 return (retval); 2933 } 2934 2935 /* ARGSUSED */ 2936 static void 2937 rmclomv_checkrmc(caddr_t arg) 2938 { 2939 callb_cpr_t cprinfo; 2940 int err; 2941 int retries; 2942 int state; 2943 dp_get_sysinfo_r_t sysinfo; 2944 2945 CALLB_CPR_INIT(&cprinfo, &rmclomv_checkrmc_lock, callb_generic_cpr, 2946 "rmclomv_checkrmc"); 2947 2948 mutex_enter(&rmclomv_checkrmc_lock); 2949 for (;;) { 2950 /* 2951 * Initial entry to this for loop is made with 2952 * rmclomv_checkrmc_sig set to RMCLOMV_PROCESS_NOW. So the 2953 * following while loop drops through the first time. A 2954 * timeout call is made just before polling the RMC. Its 2955 * interrupt routine sustains this loop by injecting additional 2956 * state changes and cv events. 2957 */ 2958 /* 2959 * Wait for someone to tell me to continue. 2960 */ 2961 while (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_WAIT) { 2962 CALLB_CPR_SAFE_BEGIN(&cprinfo); 2963 cv_wait(&rmclomv_checkrmc_sig_cv, 2964 &rmclomv_checkrmc_lock); 2965 CALLB_CPR_SAFE_END(&cprinfo, &rmclomv_checkrmc_lock); 2966 } 2967 2968 mutex_exit(&rmclomv_checkrmc_lock); 2969 /* 2970 * mustn't hold same lock as timeout called with 2971 * when cancelling timer 2972 */ 2973 if (timer_id != 0) { 2974 (void) untimeout(timer_id); 2975 timer_id = 0; 2976 } 2977 mutex_enter(&rmclomv_checkrmc_lock); 2978 2979 /* RMCLOMV_CHECKRMC_EXITNOW implies signal by _detach(). */ 2980 if (rmclomv_checkrmc_sig == RMCLOMV_CHECKRMC_EXITNOW) { 2981 rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT; 2982 2983 /* rmclomv_checkrmc_lock is held at this point! */ 2984 CALLB_CPR_EXIT(&cprinfo); 2985 2986 thread_exit(); 2987 /* NOTREACHED */ 2988 } 2989 2990 rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_WAIT; 2991 2992 /* 2993 * If the RMC is not responding, rmclomv_do_cmd() takes a 2994 * long time and eventually times out. We conclude that the 2995 * RMC is broken if it doesn't respond to a number of polls 2996 * made 60 secs apart. So that the rmclomv_do_cmd() time-out 2997 * period isn't added to our 60 second timer, make the 2998 * timeout() call before calling rmclomv_do_cmd(). 2999 */ 3000 if (timer_id == 0) { 3001 timer_id = timeout(rmclomv_checkrmc_wakeup, NULL, 3002 60 * drv_usectohz(1000000)); 3003 } 3004 3005 mutex_exit(&rmclomv_checkrmc_lock); 3006 3007 err = rmclomv_do_cmd(DP_GET_SYSINFO, DP_GET_SYSINFO_R, 3008 sizeof (sysinfo), (intptr_t)NULL, (intptr_t)&sysinfo); 3009 if (err == 0) { 3010 mutex_enter(&rmclomv_state_lock); 3011 state = rmclomv_rmc_state; 3012 /* successful poll, reset fail count */ 3013 rmclomv_rmcfailcount = 0; 3014 mutex_exit(&rmclomv_state_lock); 3015 3016 if (state != RMCLOMV_RMCSTATE_OK) { 3017 rmclomv_refresh_wakeup(); 3018 } 3019 } 3020 if ((err != 0) && 3021 (rmclomv_rmc_error != RMCLOMV_RMCSTATE_DOWNLOAD)) { 3022 /* 3023 * Failed response or no response from RMC. 3024 * Count the failure. 3025 * If threshold exceeded, send a DR event. 3026 */ 3027 mutex_enter(&rmclomv_state_lock); 3028 retries = rmclomv_rmcfailcount; 3029 state = rmclomv_rmc_state; 3030 if (retries == RMCLOMV_RMCFAILTHRESHOLD) 3031 rmclomv_rmc_state = RMCLOMV_RMCSTATE_FAILED; 3032 if (rmclomv_rmcfailcount <= RMCLOMV_RMCFAILTHRESHOLD) 3033 rmclomv_rmcfailcount++; 3034 mutex_exit(&rmclomv_state_lock); 3035 3036 if (retries == RMCLOMV_RMCFAILTHRESHOLD) { 3037 cmn_err(CE_WARN, "SC %s responding", 3038 state == RMCLOMV_RMCSTATE_OK ? 3039 "has stopped" : "is not"); 3040 refresh_name_cache(B_TRUE); 3041 rmclomv_dr_data_handler(str_sc, SE_NO_HINT); 3042 } 3043 } 3044 3045 /* 3046 * Re-enter the lock to prepare for another iteration. 3047 * We must have the lock here to protect rmclomv_checkrmc_sig. 3048 */ 3049 mutex_enter(&rmclomv_checkrmc_lock); 3050 } 3051 } 3052 3053 static void 3054 rmclomv_checkrmc_start(void) 3055 { 3056 kthread_t *tp; 3057 3058 mutex_enter(&rmclomv_checkrmc_lock); 3059 3060 if (rmclomv_checkrmc_tid == 0) { 3061 rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_PROCESSNOW; 3062 3063 tp = thread_create(NULL, 0, rmclomv_checkrmc, NULL, 0, 3064 &p0, TS_RUN, maxclsyspri); 3065 rmclomv_checkrmc_tid = tp->t_did; 3066 } 3067 3068 mutex_exit(&rmclomv_checkrmc_lock); 3069 } 3070 3071 static void 3072 rmclomv_checkrmc_destroy(void) 3073 { 3074 kt_did_t tid; 3075 3076 mutex_enter(&rmclomv_checkrmc_lock); 3077 tid = rmclomv_checkrmc_tid; 3078 if (tid != 0) { 3079 rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_EXITNOW; 3080 cv_signal(&rmclomv_checkrmc_sig_cv); 3081 rmclomv_checkrmc_tid = 0; 3082 } 3083 mutex_exit(&rmclomv_checkrmc_lock); 3084 3085 /* 3086 * Wait for rmclomv_checkrmc() to finish 3087 */ 3088 if (tid != 0) 3089 thread_join(tid); 3090 } 3091 3092 /*ARGSUSED*/ 3093 static void 3094 rmclomv_checkrmc_wakeup(void *arg) 3095 { 3096 mutex_enter(&rmclomv_checkrmc_lock); 3097 3098 if (rmclomv_checkrmc_sig != RMCLOMV_CHECKRMC_EXITNOW) 3099 rmclomv_checkrmc_sig = RMCLOMV_CHECKRMC_PROCESSNOW; 3100 cv_signal(&rmclomv_checkrmc_sig_cv); 3101 3102 mutex_exit(&rmclomv_checkrmc_lock); 3103 } 3104 3105 /* ARGSUSED */ 3106 static void 3107 rmclomv_refresh(caddr_t arg) 3108 { 3109 void (*plat_nodename_set_fun)(void); 3110 sig_state_t *current_sgn_p; 3111 callb_cpr_t cprinfo; 3112 int state; 3113 int tmp_checkrmc_sig; 3114 3115 CALLB_CPR_INIT(&cprinfo, &rmclomv_refresh_lock, callb_generic_cpr, 3116 "rmclomv_refresh"); 3117 3118 /* 3119 * Wait until the rmclomv_checkrmc() thread has had a chance to 3120 * run its main loop. This is done so that rmclomv_refresh will 3121 * only run its main loop once at start of day; otherwise, it may 3122 * run twice and generate warning messages when redundantly populating 3123 * its internal cache. 3124 */ 3125 do { 3126 delay(drv_usectohz(DELAY_TIME)); 3127 mutex_enter(&rmclomv_checkrmc_lock); 3128 tmp_checkrmc_sig = rmclomv_checkrmc_sig; 3129 mutex_exit(&rmclomv_checkrmc_lock); 3130 } while (tmp_checkrmc_sig != RMCLOMV_CHECKRMC_WAIT); 3131 3132 mutex_enter(&rmclomv_refresh_lock); 3133 for (;;) { 3134 3135 /* 3136 * Wait for someone to tell me to continue. 3137 */ 3138 while (rmclomv_refresh_sig == RMCLOMV_REFRESH_WAIT) { 3139 CALLB_CPR_SAFE_BEGIN(&cprinfo); 3140 cv_wait(&rmclomv_refresh_sig_cv, &rmclomv_refresh_lock); 3141 CALLB_CPR_SAFE_END(&cprinfo, &rmclomv_refresh_lock); 3142 } 3143 3144 /* RMCLOMV_REFRESH_EXITNOW implies signal by _detach(). */ 3145 if (rmclomv_refresh_sig == RMCLOMV_REFRESH_EXITNOW) { 3146 rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT; 3147 3148 /* rmclomv_refresh_lock is held at this point! */ 3149 CALLB_CPR_EXIT(&cprinfo); 3150 3151 thread_exit(); 3152 /* NOTREACHED */ 3153 } 3154 3155 ASSERT(rmclomv_refresh_sig == RMCLOMV_REFRESH_PROCESSNOW); 3156 rmclomv_refresh_sig = RMCLOMV_REFRESH_WAIT; 3157 3158 mutex_exit(&rmclomv_refresh_lock); 3159 3160 refresh_name_cache(B_FALSE); 3161 3162 /* 3163 * We're not going to access rmclomv_sysinfo_data here, 3164 * so there's no point in locking it before reading 3165 * rmclomv_sysinfo_valid. Also this avoids holding two 3166 * locks at once and the concommitant worry about deadlocks. 3167 */ 3168 if (rmclomv_sysinfo_valid) { 3169 /* 3170 * We've just successfully read the RMC sysinfo 3171 * so the RMC must be operational. Update its 3172 * state and if it was previously not OK, refresh 3173 * nodename, CPU signatures and watchdog settings. 3174 */ 3175 mutex_enter(&rmclomv_state_lock); 3176 rmclomv_rmcfailcount = 0; 3177 state = rmclomv_rmc_state; 3178 rmclomv_rmc_state = RMCLOMV_RMCSTATE_OK; 3179 mutex_exit(&rmclomv_state_lock); 3180 3181 if (state != RMCLOMV_RMCSTATE_OK) { 3182 rmclomv_dr_data_handler(str_sc, SE_NO_HINT); 3183 if (state == RMCLOMV_RMCSTATE_FAILED) { 3184 cmn_err(CE_NOTE, "SC recovered"); 3185 } 3186 } 3187 3188 if (utsname.nodename[0] != 0) { 3189 plat_nodename_set_fun = 3190 (void (*)(void))modgetsymvalue( 3191 "plat_nodename_set", 0); 3192 if (plat_nodename_set_fun != NULL) 3193 plat_nodename_set_fun(); 3194 } 3195 3196 current_sgn_p = (sig_state_t *)modgetsymvalue( 3197 "current_sgn", 0); 3198 3199 /* 3200 * Delay before calling CPU_SIGNATURE, to allow 3201 * any pending asynchronous communications (i.e. 3202 * plat_timesync()) to complete. This helps to 3203 * prevent the situation where the message associated 3204 * with the CPU_SIGNATURE state cannot be sent to the 3205 * system controller. 3206 */ 3207 if ((current_sgn_p != NULL) && 3208 (current_sgn_p->state_t.sig != 0)) { 3209 delay(drv_usectohz(CPU_SIGNATURE_DELAY_TIME)); 3210 CPU_SIGNATURE(current_sgn_p->state_t.sig, 3211 current_sgn_p->state_t.state, 3212 current_sgn_p->state_t.sub_state, -1); 3213 3214 if (!(boothowto & RB_DEBUG)) { 3215 /* 3216 * Delay before calling 3217 * send_watchdog_msg, to allow 3218 * CPU_SIGNATURE() time to 3219 * complete; this increases the 3220 * chances of successfully sending 3221 * the watchdog message to the 3222 * system controller. 3223 */ 3224 delay(drv_usectohz( 3225 CPU_SIGNATURE_DELAY_TIME)); 3226 send_watchdog_msg(last_watchdog_msg); 3227 } 3228 } 3229 } 3230 3231 /* 3232 * update keyswitch value in case it changed while the 3233 * RMC was out of action 3234 */ 3235 LOCK_CACHE 3236 if (rmclomv_sysinfo_valid) { 3237 real_key_position = rmclomv_sysinfo_data.keyswitch; 3238 if ((real_key_position != RMC_KEYSWITCH_POS_UNKNOWN) && 3239 (real_key_position <= RMC_KEYSWITCH_POS_OFF)) { 3240 key_position = real_key_position; 3241 } else { 3242 /* treat unknown key position as locked */ 3243 key_position = RMC_KEYSWITCH_POS_LOCKED; 3244 } 3245 } else { 3246 /* treat unreadable key position as locked */ 3247 key_position = RMC_KEYSWITCH_POS_LOCKED; 3248 real_key_position = RMC_KEYSWITCH_POS_UNKNOWN; 3249 } 3250 RELEASE_CACHE 3251 3252 /* 3253 * Re-enter the lock to prepare for another iteration. 3254 * We must have the lock here to protect rmclomv_refresh_sig. 3255 */ 3256 mutex_enter(&rmclomv_refresh_lock); 3257 } 3258 } 3259 3260 static void 3261 rmclomv_refresh_start(void) 3262 { 3263 kthread_t *tp; 3264 3265 mutex_enter(&rmclomv_refresh_lock); 3266 3267 if (rmclomv_refresh_tid == 0) { 3268 rmclomv_refresh_sig = RMCLOMV_REFRESH_PROCESSNOW; 3269 3270 tp = thread_create(NULL, 0, rmclomv_refresh, NULL, 0, 3271 &p0, TS_RUN, maxclsyspri); 3272 rmclomv_refresh_tid = tp->t_did; 3273 } 3274 3275 mutex_exit(&rmclomv_refresh_lock); 3276 } 3277 3278 static void 3279 rmclomv_refresh_destroy(void) 3280 { 3281 kt_did_t tid; 3282 3283 mutex_enter(&rmclomv_refresh_lock); 3284 tid = rmclomv_refresh_tid; 3285 if (tid != 0) { 3286 rmclomv_refresh_sig = RMCLOMV_REFRESH_EXITNOW; 3287 cv_signal(&rmclomv_refresh_sig_cv); 3288 rmclomv_refresh_tid = 0; 3289 } 3290 mutex_exit(&rmclomv_refresh_lock); 3291 3292 /* 3293 * Wait for rmclomv_refresh() to finish 3294 */ 3295 if (tid != 0) 3296 thread_join(tid); 3297 } 3298 3299 static void 3300 rmclomv_refresh_wakeup(void) 3301 { 3302 mutex_enter(&rmclomv_refresh_lock); 3303 3304 if (rmclomv_refresh_sig != RMCLOMV_REFRESH_EXITNOW) 3305 rmclomv_refresh_sig = RMCLOMV_REFRESH_PROCESSNOW; 3306 cv_signal(&rmclomv_refresh_sig_cv); 3307 3308 mutex_exit(&rmclomv_refresh_lock); 3309 } 3310 3311 static void 3312 send_watchdog_msg(int msg) 3313 { 3314 rmc_comm_msg_t request; 3315 dp_set_host_watchdog_t watchdog_msg; 3316 3317 if (rmclomv_watchdog_mode) 3318 return; 3319 3320 watchdog_msg.enable = msg; 3321 request.msg_type = DP_SET_HOST_WATCHDOG; 3322 request.msg_len = sizeof (watchdog_msg); 3323 request.msg_buf = (caddr_t)&watchdog_msg; 3324 (void) rmc_comm_request_nowait(&request, (msg == 1) ? 3325 RMC_COMM_DREQ_URGENT : 0); 3326 } 3327 3328 /*ARGSUSED*/ 3329 static uint_t 3330 rmc_set_watchdog_timer(uint_t timeoutval) 3331 { 3332 ASSERT(MUTEX_HELD(&tod_lock)); 3333 3334 if ((watchdog_enable == 0) || (watchdog_available == 0)) { 3335 return (0); 3336 } 3337 3338 /* 3339 * If boothowto has RB_DEBUG set we never want to set the watchdog 3340 * support on. 3341 */ 3342 if (boothowto & RB_DEBUG) { 3343 return (0); 3344 } 3345 3346 /* 3347 * When the watchdog is shut off last_watchdog_msg goes from a 3348 * 0 to a 1. So we must test to see that last_watchdog_msg is 3349 * set to 1 indicating that watchdog was shut off and 3350 * After which we set last_watchdog_msg back to 0 so that we do not 3351 * run this code 3352 * again. 3353 */ 3354 if (last_watchdog_msg == 1) { 3355 send_watchdog_msg(0); 3356 last_watchdog_msg = 0; 3357 } 3358 3359 pmugpio_watchdog_pat(); 3360 3361 watchdog_activated = 1; 3362 3363 return (1); 3364 } 3365 3366 static uint_t 3367 rmc_clear_watchdog_timer(void) 3368 { 3369 ASSERT(MUTEX_HELD(&tod_lock)); 3370 if ((watchdog_activated == 0) || (boothowto & RB_DEBUG)) 3371 return (0); 3372 3373 send_watchdog_msg(1); 3374 last_watchdog_msg = 1; 3375 watchdog_activated = 0; 3376 3377 return (0); 3378 } 3379 3380 static void 3381 plat_timesync(void *arg) 3382 { 3383 timestruc_t now; 3384 todinfo_t tod; 3385 rmc_comm_msg_t request; 3386 dp_set_date_time_t set_time_msg; 3387 int retval; 3388 timestruc_t ts; 3389 dp_get_date_time_r_t *date_and_time_info; 3390 int buffer[DATE_TIME_MSG_SIZE]; 3391 3392 /* Is the system coming up? */ 3393 if (arg != NULL) { 3394 /* Request the time from the RMC clock. */ 3395 retval = rmclomv_do_cmd(DP_GET_DATE_TIME, DP_GET_DATE_TIME_R, 3396 DATE_TIME_MSG_SIZE, (intptr_t)NULL, (intptr_t)&buffer); 3397 3398 /* 3399 * If we were able to get the time lets set the local clock. 3400 * The time returned from RMC is in Unix time format. 3401 * 3402 * If we couldn't get the time we'll accept the drift so as not 3403 * to cause congestion on the I2C bus or cause boot 3404 * performance regressions. 3405 */ 3406 if (retval == RCNOERR) { 3407 date_and_time_info = (dp_get_date_time_r_t *)buffer; 3408 ts.tv_sec = date_and_time_info->current_datetime; 3409 ts.tv_nsec = 0; 3410 mutex_enter(&tod_lock); 3411 tod_set(ts); 3412 set_hrestime(&ts); 3413 mutex_exit(&tod_lock); 3414 } 3415 } 3416 3417 gethrestime(&now); 3418 mutex_enter(&tod_lock); 3419 tod = utc_to_tod(now.tv_sec); 3420 mutex_exit(&tod_lock); 3421 3422 set_time_msg.year = tod.tod_year; 3423 set_time_msg.month = tod.tod_month - 1; 3424 set_time_msg.day = tod.tod_day; 3425 set_time_msg.hour = tod.tod_hour; 3426 set_time_msg.minute = tod.tod_min; 3427 set_time_msg.second = tod.tod_sec; 3428 3429 request.msg_type = DP_SET_DATE_TIME; 3430 request.msg_len = sizeof (set_time_msg); 3431 request.msg_buf = (caddr_t)&set_time_msg; 3432 3433 (void) rmc_comm_request_nowait(&request, 0); 3434 3435 mutex_enter(×ync_lock); 3436 if (timesync_interval != 0) 3437 timesync_tid = timeout(plat_timesync, NULL, timesync_interval); 3438 mutex_exit(×ync_lock); 3439 } 3440 3441 /* 3442 * Interfaces to get/set alarm relays from outside 3443 */ 3444 int 3445 rmclomv_alarm_get(int alarm_type, int *alarm_state) 3446 { 3447 rmclomv_cache_section_t *section; 3448 int index; 3449 uint16_t sensor_status; 3450 dp_get_alarm_state_t u_rmc_alarm; 3451 dp_get_alarm_state_r_t u_rmc_alarm_r; 3452 3453 /* see if we've got ALARM handles cached */ 3454 LOCK_CACHE 3455 sensor_status = ENVMON_SENSOR_OK; 3456 3457 if ((rmclomv_cache_valid == B_FALSE) || 3458 ((section = rmclomv_find_section(rmclomv_cache, 3459 RMCLOMV_ALARM_IND)) == NULL)) { 3460 sensor_status = ENVMON_NOT_PRESENT; 3461 } 3462 if (sensor_status == ENVMON_SENSOR_OK) { 3463 /* 3464 * user correctly identified a ALARM, note its 3465 * handle value and request the ALARM status 3466 */ 3467 index = alarm_type; 3468 if (index >= section->num_entries) 3469 sensor_status = ENVMON_INACCESSIBLE; 3470 else 3471 u_rmc_alarm.handle = section->entry[index].handle; 3472 } 3473 RELEASE_CACHE 3474 if ((sensor_status == ENVMON_SENSOR_OK) && (rmclomv_rmc_error || 3475 rmclomv_do_cmd(DP_GET_ALARM_STATE, DP_GET_ALARM_STATE_R, 3476 sizeof (u_rmc_alarm_r), (intptr_t)&u_rmc_alarm, 3477 (intptr_t)&u_rmc_alarm_r) != 0)) { 3478 sensor_status = ENVMON_INACCESSIBLE; 3479 } 3480 if (sensor_status == ENVMON_SENSOR_OK) { 3481 /* 3482 * copy results into buffer for user 3483 * start with some defaults then override 3484 */ 3485 *alarm_state = 0; 3486 3487 if (u_rmc_alarm_r.alarm_state[0].sensor_status != 3488 DP_SENSOR_DATA_AVAILABLE) 3489 return (ENXIO); 3490 else { 3491 dp_alarm_state_t alarmState; 3492 alarmState = u_rmc_alarm_r.alarm_state[0]; 3493 3494 switch (alarmState.state) { 3495 case DP_ALARM_OFF: 3496 break; 3497 case DP_ALARM_ON: 3498 *alarm_state = 1; 3499 break; 3500 default: 3501 break; 3502 } 3503 } 3504 } else 3505 return (ENXIO); 3506 3507 return (0); 3508 } 3509 3510 int 3511 rmclomv_alarm_set(int alarm_type, int new_state) 3512 { 3513 rmclomv_cache_section_t *section; 3514 int index; 3515 uint16_t sensor_status; 3516 dp_set_alarm_state_t u_rmc_setalarm; 3517 dp_set_alarm_state_r_t u_rmc_setalarm_r; 3518 3519 /* see if we've got ALARM handles cached */ 3520 LOCK_CACHE 3521 sensor_status = ENVMON_SENSOR_OK; 3522 3523 if ((rmclomv_cache_valid == B_FALSE) || 3524 ((section = rmclomv_find_section(rmclomv_cache, 3525 RMCLOMV_ALARM_IND)) == NULL)) { 3526 sensor_status = ENVMON_NOT_PRESENT; 3527 } 3528 if (sensor_status == ENVMON_SENSOR_OK) { 3529 /* 3530 * user correctly identified a ALARM, note its 3531 * handle value and request the ALARM status 3532 */ 3533 index = alarm_type; 3534 if (index >= section->num_entries) 3535 sensor_status = ENVMON_INACCESSIBLE; 3536 else { 3537 u_rmc_setalarm.handle = section->entry[index].handle; 3538 u_rmc_setalarm.state = new_state; 3539 } 3540 } 3541 RELEASE_CACHE 3542 if ((sensor_status == ENVMON_SENSOR_OK) && 3543 (rmclomv_rmc_error || 3544 rmclomv_do_cmd(DP_SET_ALARM_STATE, DP_SET_ALARM_STATE_R, 3545 sizeof (u_rmc_setalarm_r), (intptr_t)&u_rmc_setalarm, 3546 (intptr_t)&u_rmc_setalarm_r) != 0)) { 3547 sensor_status = ENVMON_INACCESSIBLE; 3548 } 3549 3550 if (u_rmc_setalarm_r.status != DP_SET_ALARM_OK) { 3551 return (EIO); 3552 } 3553 3554 if (sensor_status != ENVMON_SENSOR_OK) { 3555 return (ENXIO); 3556 } 3557 3558 return (0); 3559 } 3560