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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Enclosure Services Devices, SAF-TE Enclosure Routines 24 * 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 26 * Use is subject to license terms. 27 */ 28 29 #include <sys/modctl.h> 30 #include <sys/file.h> 31 #include <sys/scsi/scsi.h> 32 #include <sys/stat.h> 33 #include <sys/scsi/targets/sesio.h> 34 #include <sys/scsi/targets/ses.h> 35 36 37 static int set_objstat_sel(ses_softc_t *, ses_objarg *, int); 38 static int wrbuf16(ses_softc_t *, uchar_t, uchar_t, uchar_t, uchar_t, int); 39 static void wrslot_stat(ses_softc_t *, int); 40 static int perf_slotop(ses_softc_t *, uchar_t, uchar_t, int); 41 42 #define ALL_ENC_STAT \ 43 (ENCSTAT_CRITICAL|ENCSTAT_UNRECOV|ENCSTAT_NONCRITICAL|ENCSTAT_INFO) 44 45 #define SCRATCH 64 46 #define NPSEUDO_THERM 1 47 #define NPSEUDO_ALARM 1 48 struct scfg { 49 /* 50 * Cached Configuration 51 */ 52 uchar_t Nfans; /* Number of Fans */ 53 uchar_t Npwr; /* Number of Power Supplies */ 54 uchar_t Nslots; /* Number of Device Slots */ 55 uchar_t DoorLock; /* Door Lock Installed */ 56 uchar_t Ntherm; /* Number of Temperature Sensors */ 57 uchar_t Nspkrs; /* Number of Speakers */ 58 uchar_t Nalarm; /* Number of Alarms (at least one) */ 59 /* 60 * Cached Flag Bytes for Global Status 61 */ 62 uchar_t flag1; 63 uchar_t flag2; 64 /* 65 * What object index ID is where various slots start. 66 */ 67 uchar_t pwroff; 68 uchar_t slotoff; 69 #define ALARM_OFFSET(cc) (cc)->slotoff - 1 70 }; 71 #define FLG1_ALARM 0x1 72 #define FLG1_GLOBFAIL 0x2 73 #define FLG1_GLOBWARN 0x4 74 #define FLG1_ENCPWROFF 0x8 75 #define FLG1_ENCFANFAIL 0x10 76 #define FLG1_ENCPWRFAIL 0x20 77 #define FLG1_ENCDRVFAIL 0x40 78 #define FLG1_ENCDRVWARN 0x80 79 80 #define FLG2_LOCKDOOR 0x4 81 #define SAFTE_PRIVATE sizeof (struct scfg) 82 83 #if !defined(lint) 84 _NOTE(MUTEX_PROTECTS_DATA(scsi_device::sd_mutex, scfg)) 85 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nfans)) 86 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Npwr)) 87 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nslots)) 88 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::DoorLock)) 89 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Ntherm)) 90 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nspkrs)) 91 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nalarm)) 92 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::flag1)) 93 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::flag2)) 94 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::pwroff)) 95 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::slotoff)) 96 #endif 97 98 static int 99 safte_getconfig(ses_softc_t *ssc) 100 { 101 struct scfg *cfg; 102 int err; 103 Uscmd local, *lp = &local; 104 char rqbuf[SENSE_LENGTH], *sdata; 105 static char cdb[CDB_GROUP1] = 106 { SCMD_READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SCRATCH, 0 }; 107 108 cfg = ssc->ses_private; 109 if (cfg == NULL) 110 return (ENXIO); 111 112 sdata = kmem_alloc(SCRATCH, KM_SLEEP); 113 if (sdata == NULL) 114 return (ENOMEM); 115 116 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE; 117 lp->uscsi_timeout = ses_io_time; 118 lp->uscsi_cdb = cdb; 119 lp->uscsi_bufaddr = sdata; 120 lp->uscsi_buflen = SCRATCH; 121 lp->uscsi_cdblen = sizeof (cdb); 122 lp->uscsi_rqbuf = rqbuf; 123 lp->uscsi_rqlen = sizeof (rqbuf); 124 125 err = ses_runcmd(ssc, lp); 126 if (err) { 127 kmem_free(sdata, SCRATCH); 128 return (err); 129 } 130 131 if ((lp->uscsi_buflen - lp->uscsi_resid) < 6) { 132 SES_LOG(ssc, CE_NOTE, "Too little data (%ld) for configuration", 133 lp->uscsi_buflen - lp->uscsi_resid); 134 kmem_free(sdata, SCRATCH); 135 return (EIO); 136 } 137 SES_LOG(ssc, SES_CE_DEBUG1, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm " 138 "%d Nspkrs %d", sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], 139 sdata[5]); 140 141 mutex_enter(&ssc->ses_devp->sd_mutex); 142 cfg->Nfans = sdata[0]; 143 cfg->Npwr = sdata[1]; 144 cfg->Nslots = sdata[2]; 145 cfg->DoorLock = sdata[3]; 146 cfg->Ntherm = sdata[4]; 147 cfg->Nspkrs = sdata[5]; 148 cfg->Nalarm = NPSEUDO_ALARM; 149 mutex_exit(&ssc->ses_devp->sd_mutex); 150 kmem_free(sdata, SCRATCH); 151 return (0); 152 } 153 154 int 155 safte_softc_init(ses_softc_t *ssc, int doinit) 156 { 157 int r, i; 158 struct scfg *cc; 159 160 if (doinit == 0) { 161 mutex_enter(&ssc->ses_devp->sd_mutex); 162 if (ssc->ses_nobjects) { 163 if (ssc->ses_objmap) { 164 kmem_free(ssc->ses_objmap, 165 ssc->ses_nobjects * sizeof (encobj)); 166 ssc->ses_objmap = NULL; 167 } 168 ssc->ses_nobjects = 0; 169 } 170 if (ssc->ses_private) { 171 kmem_free(ssc->ses_private, SAFTE_PRIVATE); 172 ssc->ses_private = NULL; 173 } 174 mutex_exit(&ssc->ses_devp->sd_mutex); 175 return (0); 176 } 177 178 mutex_enter(&ssc->ses_devp->sd_mutex); 179 if (ssc->ses_private == NULL) { 180 ssc->ses_private = kmem_zalloc(SAFTE_PRIVATE, KM_SLEEP); 181 if (ssc->ses_private == NULL) { 182 mutex_exit(&ssc->ses_devp->sd_mutex); 183 return (ENOMEM); 184 } 185 } 186 187 ssc->ses_nobjects = 0; 188 ssc->ses_encstat = 0; 189 mutex_exit(&ssc->ses_devp->sd_mutex); 190 191 if ((r = safte_getconfig(ssc)) != 0) { 192 return (r); 193 } 194 195 /* 196 * The number of objects here, as well as that reported by the 197 * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15) 198 * that get reported during READ_BUFFER/READ_ENC_STATUS. 199 */ 200 mutex_enter(&ssc->ses_devp->sd_mutex); 201 cc = ssc->ses_private; 202 ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock + 203 cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM; 204 ssc->ses_objmap = (encobj *) 205 kmem_zalloc(ssc->ses_nobjects * sizeof (encobj), KM_SLEEP); 206 mutex_exit(&ssc->ses_devp->sd_mutex); 207 if (ssc->ses_objmap == NULL) 208 return (ENOMEM); 209 r = 0; 210 /* 211 * Note that this is all arranged for the convenience 212 * in later fetches of status. 213 */ 214 mutex_enter(&ssc->ses_devp->sd_mutex); 215 for (i = 0; i < cc->Nfans; i++) 216 ssc->ses_objmap[r++].enctype = SESTYP_FAN; 217 cc->pwroff = (uchar_t)r; 218 for (i = 0; i < cc->Npwr; i++) 219 ssc->ses_objmap[r++].enctype = SESTYP_POWER; 220 for (i = 0; i < cc->DoorLock; i++) 221 ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK; 222 for (i = 0; i < cc->Nspkrs; i++) 223 ssc->ses_objmap[r++].enctype = SESTYP_ALARM; 224 for (i = 0; i < cc->Ntherm; i++) 225 ssc->ses_objmap[r++].enctype = SESTYP_THERM; 226 for (i = 0; i < NPSEUDO_THERM; i++) 227 ssc->ses_objmap[r++].enctype = SESTYP_THERM; 228 ssc->ses_objmap[r++].enctype = SESTYP_ALARM; 229 cc->slotoff = (uchar_t)r; 230 for (i = 0; i < cc->Nslots; i++) 231 ssc->ses_objmap[r++].enctype = SESTYP_DEVICE; 232 mutex_exit(&ssc->ses_devp->sd_mutex); 233 return (0); 234 } 235 236 int 237 safte_init_enc(ses_softc_t *ssc) 238 { 239 int err; 240 Uscmd local, *lp = &local; 241 char rqbuf[SENSE_LENGTH], *sdata; 242 static char cdb0[CDB_GROUP1] = { SCMD_SDIAG }; 243 static char cdb[CDB_GROUP1] = 244 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SCRATCH, 0 }; 245 246 sdata = kmem_alloc(SCRATCH, KM_SLEEP); 247 lp->uscsi_flags = USCSI_RQENABLE; 248 lp->uscsi_timeout = ses_io_time; 249 lp->uscsi_cdb = cdb0; 250 lp->uscsi_bufaddr = NULL; 251 lp->uscsi_buflen = 0; 252 lp->uscsi_cdblen = sizeof (cdb0); 253 lp->uscsi_rqbuf = rqbuf; 254 lp->uscsi_rqlen = sizeof (rqbuf); 255 err = ses_runcmd(ssc, lp); 256 if (err) { 257 kmem_free(sdata, SCRATCH); 258 return (err); 259 } 260 261 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE; 262 lp->uscsi_timeout = ses_io_time; 263 lp->uscsi_cdb = cdb; 264 lp->uscsi_bufaddr = sdata; 265 lp->uscsi_buflen = SCRATCH; 266 lp->uscsi_cdblen = sizeof (cdb); 267 lp->uscsi_rqbuf = rqbuf; 268 lp->uscsi_rqlen = sizeof (rqbuf); 269 bzero(&sdata[1], 15); 270 sdata[0] = SAFTE_WT_GLOBAL; 271 err = ses_runcmd(ssc, lp); 272 kmem_free(sdata, SCRATCH); 273 return (err); 274 } 275 276 277 static char *toolittle = "Too Little Data Returned (%d) at line %d"; 278 #define BAIL(r, x, k, l, m, n) \ 279 if (r >= x) { \ 280 SES_LOG(ssc, CE_NOTE, toolittle, x, __LINE__); \ 281 kmem_free(k, l); \ 282 kmem_free(m, n); \ 283 return (EIO); \ 284 } 285 286 static int 287 safte_rdstat(ses_softc_t *ssc, int slpflg) 288 { 289 int err, oid, r, i, hiwater, nitems; 290 ushort_t tempflags; 291 size_t buflen; 292 uchar_t status, oencstat; 293 Uscmd local, *lp = &local; 294 struct scfg *cc = ssc->ses_private; 295 char rqbuf[SENSE_LENGTH], *sdata; 296 char cdb[CDB_GROUP1]; 297 int *driveids, id_size = cc->Nslots * sizeof (int); 298 299 driveids = kmem_alloc(id_size, slpflg); 300 if (driveids == NULL) { 301 return (ENOMEM); 302 } 303 304 /* 305 * The number of bytes of data we need to get is 306 * Nfans + Npwr + Nslots + Nspkrs + Ntherm + nochoice 307 * (nochoice = 1 doorlock + 1 spkr + 2 pseudo therms + 10 extra) 308 * the extra are simply for good luck. 309 */ 310 buflen = cc->Nfans + cc->Npwr + cc->Nslots + cc->Nspkrs; 311 buflen += cc->Ntherm + 14; 312 313 /* 314 * Towards the end of this function this buffer is reused. 315 * Thus we need to make sure that we have allocated enough 316 * memory retrieving buffer 1 & 4. 317 * buffer 1 -> element status & drive id 318 * buffer 4 -> drive status & drive command history. 319 * buffer 4 uses 4 bytes per drive bay. 320 */ 321 322 if (buflen < cc->Nslots * 4) { 323 buflen = cc->Nslots * 4; 324 } 325 326 if (ssc->ses_nobjects > buflen) 327 buflen = ssc->ses_nobjects; 328 329 if (buflen > 0xffff) { 330 cmn_err(CE_WARN, "Illogical SCSI data"); 331 kmem_free(driveids, id_size); 332 return (EIO); 333 } 334 335 sdata = kmem_alloc(buflen, slpflg); 336 if (sdata == NULL) { 337 kmem_free(driveids, id_size); 338 return (ENOMEM); 339 } 340 341 cdb[0] = SCMD_READ_BUFFER; 342 cdb[1] = 1; 343 cdb[2] = SAFTE_RD_RDESTS; 344 cdb[3] = 0; 345 cdb[4] = 0; 346 cdb[5] = 0; 347 cdb[6] = 0; 348 cdb[7] = (buflen >> 8) & 0xff; 349 cdb[8] = buflen & 0xff; 350 cdb[9] = 0; 351 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE; 352 lp->uscsi_timeout = ses_io_time; 353 lp->uscsi_cdb = cdb; 354 lp->uscsi_bufaddr = sdata; 355 lp->uscsi_buflen = buflen; 356 lp->uscsi_cdblen = sizeof (cdb); 357 lp->uscsi_rqbuf = rqbuf; 358 lp->uscsi_rqlen = sizeof (rqbuf); 359 360 err = ses_runcmd(ssc, lp); 361 if (err) { 362 kmem_free(sdata, buflen); 363 kmem_free(driveids, id_size); 364 return (err); 365 } 366 367 hiwater = lp->uscsi_buflen - lp->uscsi_resid; 368 369 /* 370 * invalidate all status bits. 371 */ 372 mutex_enter(&ssc->ses_devp->sd_mutex); 373 for (i = 0; i < ssc->ses_nobjects; i++) 374 ssc->ses_objmap[i].svalid = 0; 375 oencstat = ssc->ses_encstat & ALL_ENC_STAT; 376 ssc->ses_encstat = 0; 377 mutex_exit(&ssc->ses_devp->sd_mutex); 378 379 /* 380 * Now parse returned buffer. 381 * If we didn't get enough data back, 382 * that's considered a fatal error. 383 */ 384 oid = r = 0; 385 386 for (nitems = i = 0; i < cc->Nfans; i++) { 387 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 388 /* 389 * 0 = Fan Operational 390 * 1 = Fan is malfunctioning 391 * 2 = Fan is not present 392 * 0x80 = Unknown or Not Reportable Status 393 */ 394 mutex_enter(&ssc->ses_devp->sd_mutex); 395 ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ 396 ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ 397 switch ((uchar_t)sdata[r]) { 398 case 0: 399 nitems++; 400 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 401 /* 402 * We could get fancier and cache 403 * fan speeds that we have set, but 404 * that isn't done now. 405 */ 406 ssc->ses_objmap[oid].encstat[3] = 7; 407 break; 408 409 case 1: 410 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 411 /* 412 * FAIL and FAN STOPPED synthesized 413 */ 414 ssc->ses_objmap[oid].encstat[3] = 0x40; 415 /* 416 * Enclosure marked with CRITICAL error 417 * if only one fan or no thermometers, 418 * else NONCRIT error set. 419 */ 420 if (cc->Nfans == 1 || cc->Ntherm == 0) 421 ssc->ses_encstat |= ENCSTAT_CRITICAL; 422 else 423 ssc->ses_encstat |= ENCSTAT_NONCRITICAL; 424 break; 425 case 2: 426 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED; 427 ssc->ses_objmap[oid].encstat[3] = 0; 428 if (cc->Nfans == 1) 429 ssc->ses_encstat |= ENCSTAT_CRITICAL; 430 else 431 ssc->ses_encstat |= ENCSTAT_NONCRITICAL; 432 break; 433 case 0x80: 434 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN; 435 ssc->ses_objmap[oid].encstat[3] = 0; 436 ssc->ses_encstat |= ENCSTAT_INFO; 437 break; 438 default: 439 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 440 SES_LOG(ssc, CE_NOTE, "unknown fan%d status 0x%x", 441 i, sdata[r] & 0xff); 442 break; 443 } 444 ssc->ses_objmap[oid++].svalid = 1; 445 mutex_exit(&ssc->ses_devp->sd_mutex); 446 r++; 447 } 448 mutex_enter(&ssc->ses_devp->sd_mutex); 449 /* 450 * No matter how you cut it, no cooling elements when there 451 * should be some there is critical. 452 */ 453 if (cc->Nfans && nitems == 0) { 454 ssc->ses_encstat |= ENCSTAT_CRITICAL; 455 } 456 mutex_exit(&ssc->ses_devp->sd_mutex); 457 458 459 for (i = 0; i < cc->Npwr; i++) { 460 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 461 mutex_enter(&ssc->ses_devp->sd_mutex); 462 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 463 ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */ 464 ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */ 465 ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */ 466 switch ((uchar_t)sdata[r]) { 467 case 0x00: /* pws operational and on */ 468 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 469 break; 470 case 0x01: /* pws operational and off */ 471 ssc->ses_objmap[oid].encstat[3] = 0x10; 472 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTAVAIL; 473 ssc->ses_encstat |= ENCSTAT_INFO; 474 break; 475 case 0x10: /* pws is malfunctioning and commanded on */ 476 ssc->ses_objmap[oid].encstat[3] = 0x61; 477 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 478 if (cc->Npwr < 2) 479 ssc->ses_encstat |= ENCSTAT_CRITICAL; 480 else 481 ssc->ses_encstat |= ENCSTAT_NONCRITICAL; 482 break; 483 484 case 0x11: /* pws is malfunctioning and commanded off */ 485 ssc->ses_objmap[oid].encstat[3] = 0x51; 486 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 487 if (cc->Npwr < 2) 488 ssc->ses_encstat |= ENCSTAT_CRITICAL; 489 else 490 ssc->ses_encstat |= ENCSTAT_NONCRITICAL; 491 break; 492 case 0x20: /* pws is not present */ 493 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED; 494 ssc->ses_objmap[oid].encstat[3] = 0; 495 if (cc->Npwr < 2) 496 ssc->ses_encstat |= ENCSTAT_CRITICAL; 497 else 498 ssc->ses_encstat |= ENCSTAT_INFO; 499 break; 500 case 0x21: /* pws is present */ 501 break; 502 case 0x80: /* Unknown or Not Reportable Status */ 503 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN; 504 ssc->ses_objmap[oid].encstat[3] = 0; 505 ssc->ses_encstat |= ENCSTAT_INFO; 506 break; 507 default: 508 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 509 SES_LOG(ssc, CE_NOTE, "unknown pwr%d status 0x%x", 510 i, sdata[r] & 0xff); 511 break; 512 } 513 ssc->ses_objmap[oid++].svalid = 1; 514 mutex_exit(&ssc->ses_devp->sd_mutex); 515 r++; 516 } 517 518 /* 519 * Now I am going to save the target id's for the end of 520 * the function. (when I build the drive objects) 521 * that is when I will be getting the drive status from buffer 4 522 */ 523 524 for (i = 0; i < cc->Nslots; i++) { 525 driveids[i] = sdata[r++]; 526 } 527 528 529 530 /* 531 * We always have doorlock status, no matter what, 532 * but we only save the status if we have one. 533 */ 534 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 535 if (cc->DoorLock) { 536 /* 537 * 0 = Door Locked 538 * 1 = Door Unlocked, or no Lock Installed 539 * 0x80 = Unknown or Not Reportable Status 540 */ 541 mutex_enter(&ssc->ses_devp->sd_mutex); 542 ssc->ses_objmap[oid].encstat[1] = 0; 543 ssc->ses_objmap[oid].encstat[2] = 0; 544 switch ((uchar_t)sdata[r]) { 545 case 0: 546 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 547 ssc->ses_objmap[oid].encstat[3] = 0; 548 break; 549 case 1: 550 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 551 ssc->ses_objmap[oid].encstat[3] = 1; 552 break; 553 case 0x80: 554 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN; 555 ssc->ses_objmap[oid].encstat[3] = 0; 556 ssc->ses_encstat |= ENCSTAT_INFO; 557 break; 558 default: 559 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 560 SES_LOG(ssc, CE_NOTE, "unknown lock status 0x%x", 561 sdata[r] & 0xff); 562 break; 563 } 564 ssc->ses_objmap[oid++].svalid = 1; 565 mutex_exit(&ssc->ses_devp->sd_mutex); 566 } 567 r++; 568 569 /* 570 * We always have speaker status, no matter what, 571 * but we only save the status if we have one. 572 */ 573 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 574 if (cc->Nspkrs) { 575 mutex_enter(&ssc->ses_devp->sd_mutex); 576 ssc->ses_objmap[oid].encstat[1] = 0; 577 ssc->ses_objmap[oid].encstat[2] = 0; 578 if (sdata[r] == 1) { 579 /* 580 * We need to cache tone urgency indicators. 581 * Someday. 582 */ 583 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NONCRIT; 584 ssc->ses_objmap[oid].encstat[3] = 0x8; 585 ssc->ses_encstat |= ENCSTAT_NONCRITICAL; 586 } else if (sdata[r] == 0) { 587 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 588 ssc->ses_objmap[oid].encstat[3] = 0; 589 } else { 590 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 591 ssc->ses_objmap[oid].encstat[3] = 0; 592 SES_LOG(ssc, CE_NOTE, "unknown spkr status 0x%x", 593 sdata[r] & 0xff); 594 } 595 ssc->ses_objmap[oid++].svalid = 1; 596 mutex_exit(&ssc->ses_devp->sd_mutex); 597 } 598 r++; 599 600 for (i = 0; i < cc->Ntherm; i++) { 601 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 602 /* 603 * Status is a range from -10 to 245 deg Celsius, 604 * which we need to normalize to -20 to -235 according 605 * to the latest SCSI spec. 606 */ 607 mutex_enter(&ssc->ses_devp->sd_mutex); 608 ssc->ses_objmap[oid].encstat[1] = 0; 609 ssc->ses_objmap[oid].encstat[2] = 610 ((unsigned int) sdata[r]) - 10; 611 if (sdata[r] < 20) { 612 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 613 /* 614 * Set 'under temperature' failure. 615 */ 616 ssc->ses_objmap[oid].encstat[3] = 2; 617 ssc->ses_encstat |= ENCSTAT_CRITICAL; 618 } else if (sdata[r] > 30) { 619 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 620 /* 621 * Set 'over temperature' failure. 622 */ 623 ssc->ses_objmap[oid].encstat[3] = 8; 624 ssc->ses_encstat |= ENCSTAT_CRITICAL; 625 } else { 626 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 627 } 628 ssc->ses_objmap[oid++].svalid = 1; 629 mutex_exit(&ssc->ses_devp->sd_mutex); 630 r++; 631 } 632 633 /* 634 * Now, for "pseudo" thermometers, we have two bytes 635 * of information in enclosure status- 16 bits. Actually, 636 * the MSB is a single TEMP ALERT flag indicating whether 637 * any other bits are set, but, thanks to fuzzy thinking, 638 * in the SAF-TE spec, this can also be set even if no 639 * other bits are set, thus making this really another 640 * binary temperature sensor. 641 */ 642 643 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 644 tempflags = sdata[r++]; 645 BAIL(r, hiwater, sdata, buflen, driveids, id_size); 646 tempflags |= (tempflags << 8) | sdata[r++]; 647 mutex_enter(&ssc->ses_devp->sd_mutex); 648 649 #if NPSEUDO_THERM == 1 650 ssc->ses_objmap[oid].encstat[1] = 0; 651 if (tempflags) { 652 /* Set 'over temperature' failure. */ 653 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 654 ssc->ses_objmap[oid].encstat[3] = 8; 655 ssc->ses_encstat |= ENCSTAT_CRITICAL; 656 } else { 657 /* Set 'nominal' temperature. */ 658 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 659 } 660 ssc->ses_objmap[oid++].svalid = 1; 661 662 #else /* NPSEUDO_THERM == 1 */ 663 for (i = 0; i < NPSEUDO_THERM; i++) { 664 ssc->ses_objmap[oid].encstat[1] = 0; 665 if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) { 666 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT; 667 /* ssc->ses_objmap[oid].encstat[2] = 0; */ 668 669 /* 670 * Set 'over temperature' failure. 671 */ 672 ssc->ses_objmap[oid].encstat[3] = 8; 673 ssc->ses_encstat |= ENCSTAT_CRITICAL; 674 } else { 675 /* 676 * Set 'nominal' temperature. 677 */ 678 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 679 /* ssc->ses_objmap[oid].encstat[2] = 0; */ 680 } 681 ssc->ses_objmap[oid++].svalid = 1; 682 } 683 #endif /* NPSEUDO_THERM == 1 */ 684 685 686 /* 687 * Get alarm status. 688 */ 689 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 690 ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv; 691 ssc->ses_objmap[oid++].svalid = 1; 692 mutex_exit(&ssc->ses_devp->sd_mutex); 693 694 /* 695 * Now get drive slot status 696 */ 697 cdb[2] = SAFTE_RD_RDDSTS; 698 err = ses_runcmd(ssc, lp); 699 if (err) { 700 kmem_free(sdata, buflen); 701 kmem_free(driveids, id_size); 702 return (err); 703 } 704 hiwater = lp->uscsi_buflen - lp->uscsi_resid; 705 for (r = i = 0; i < cc->Nslots; i++, r += 4) { 706 BAIL(r+3, hiwater, sdata, buflen, driveids, id_size); 707 mutex_enter(&ssc->ses_devp->sd_mutex); 708 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED; 709 ssc->ses_objmap[oid].encstat[1] = (uchar_t)driveids[i]; 710 ssc->ses_objmap[oid].encstat[2] = 0; 711 ssc->ses_objmap[oid].encstat[3] = 0; 712 status = sdata[r+3]; 713 if ((status & 0x1) == 0) { /* no device */ 714 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED; 715 } else { 716 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK; 717 } 718 if (status & 0x2) { 719 ssc->ses_objmap[oid].encstat[2] = 0x8; 720 } 721 if ((status & 0x4) == 0) { 722 ssc->ses_objmap[oid].encstat[3] = 0x10; 723 } 724 ssc->ses_objmap[oid++].svalid = 1; 725 mutex_exit(&ssc->ses_devp->sd_mutex); 726 } 727 mutex_enter(&ssc->ses_devp->sd_mutex); 728 /* see comment below about sticky enclosure status */ 729 ssc->ses_encstat |= ENCI_SVALID | oencstat; 730 mutex_exit(&ssc->ses_devp->sd_mutex); 731 kmem_free(sdata, buflen); 732 kmem_free(driveids, id_size); 733 return (0); 734 } 735 736 int 737 safte_get_encstat(ses_softc_t *ssc, int slpflg) 738 { 739 return (safte_rdstat(ssc, slpflg)); 740 } 741 742 int 743 safte_set_encstat(ses_softc_t *ssc, uchar_t encstat, int slpflg) 744 { 745 struct scfg *cc = ssc->ses_private; 746 if (cc == NULL) 747 return (0); 748 mutex_enter(&ssc->ses_devp->sd_mutex); 749 /* 750 * Since SAF-TE devices aren't necessarily sticky in terms 751 * of state, make our soft copy of enclosure status 'sticky'- 752 * that is, things set in enclosure status stay set (as implied 753 * by conditions set in reading object status) until cleared. 754 */ 755 ssc->ses_encstat &= ~ALL_ENC_STAT; 756 ssc->ses_encstat |= (encstat & ALL_ENC_STAT); 757 ssc->ses_encstat |= ENCI_SVALID; 758 cc->flag1 &= ~(FLG1_ALARM|FLG1_GLOBFAIL|FLG1_GLOBWARN); 759 if ((encstat & (ENCSTAT_CRITICAL|ENCSTAT_UNRECOV)) != 0) { 760 cc->flag1 |= FLG1_ALARM|FLG1_GLOBFAIL; 761 } else if ((encstat & ENCSTAT_NONCRITICAL) != 0) { 762 cc->flag1 |= FLG1_GLOBWARN; 763 } 764 mutex_exit(&ssc->ses_devp->sd_mutex); 765 return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg)); 766 } 767 768 int 769 safte_get_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflg) 770 { 771 int i = (int)obp->obj_id; 772 773 if ((ssc->ses_encstat & ENCI_SVALID) == 0 || 774 (ssc->ses_objmap[i].svalid) == 0) { 775 int r = safte_rdstat(ssc, slpflg); 776 if (r) 777 return (r); 778 } 779 obp->cstat[0] = ssc->ses_objmap[i].encstat[0]; 780 obp->cstat[1] = ssc->ses_objmap[i].encstat[1]; 781 obp->cstat[2] = ssc->ses_objmap[i].encstat[2]; 782 obp->cstat[3] = ssc->ses_objmap[i].encstat[3]; 783 return (0); 784 } 785 786 787 int 788 safte_set_objstat(ses_softc_t *ssc, ses_objarg *obp, int slp) 789 { 790 int idx, err; 791 encobj *ep; 792 struct scfg *cc; 793 794 795 SES_LOG(ssc, SES_CE_DEBUG2, "safte_set_objstat(%d): %x %x %x %x", 796 (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2], 797 obp->cstat[3]); 798 799 /* 800 * If this is clear, we don't do diddly. 801 */ 802 if ((obp->cstat[0] & SESCTL_CSEL) == 0) { 803 return (0); 804 } 805 806 err = 0; 807 /* 808 * Check to see if the common bits are set and do them first. 809 */ 810 if (obp->cstat[0] & ~SESCTL_CSEL) { 811 err = set_objstat_sel(ssc, obp, slp); 812 if (err) 813 return (err); 814 } 815 816 cc = ssc->ses_private; 817 if (cc == NULL) 818 return (0); 819 820 idx = (int)obp->obj_id; 821 ep = &ssc->ses_objmap[idx]; 822 823 switch (ep->enctype) { 824 case SESTYP_DEVICE: 825 { 826 uchar_t slotop = 0; 827 /* 828 * XXX: I should probably cache the previous state 829 * XXX: of SESCTL_DEVOFF so that when it goes from 830 * XXX: true to false I can then set PREPARE FOR OPERATION 831 * XXX: flag in PERFORM SLOT OPERATION write buffer command. 832 */ 833 if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) { 834 slotop |= 0x2; 835 } 836 if (obp->cstat[2] & SESCTL_RQSID) { 837 slotop |= 0x4; 838 } 839 err = perf_slotop(ssc, (uchar_t)idx - (uchar_t)cc->slotoff, 840 slotop, slp); 841 if (err) 842 return (err); 843 mutex_enter(&ssc->ses_devp->sd_mutex); 844 if (obp->cstat[3] & SESCTL_RQSFLT) { 845 ep->priv |= 0x2; 846 } else { 847 ep->priv &= ~0x2; 848 } 849 if (ep->priv & 0xc6) { 850 ep->priv &= ~0x1; 851 } else { 852 ep->priv |= 0x1; /* no errors */ 853 } 854 mutex_exit(&ssc->ses_devp->sd_mutex); 855 wrslot_stat(ssc, slp); 856 break; 857 } 858 case SESTYP_POWER: 859 mutex_enter(&ssc->ses_devp->sd_mutex); 860 if (obp->cstat[3] & SESCTL_RQSTFAIL) { 861 cc->flag1 |= FLG1_ENCPWRFAIL; 862 } else { 863 cc->flag1 &= ~FLG1_ENCPWRFAIL; 864 } 865 mutex_exit(&ssc->ses_devp->sd_mutex); 866 err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 867 cc->flag2, 0, slp); 868 if (err) 869 return (err); 870 if (obp->cstat[3] & SESCTL_RQSTON) { 871 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, 872 idx - cc->pwroff, 0, 0, slp); 873 } else { 874 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, 875 idx - cc->pwroff, 0, 1, slp); 876 } 877 break; 878 case SESTYP_FAN: 879 mutex_enter(&ssc->ses_devp->sd_mutex); 880 if (obp->cstat[3] & SESCTL_RQSTFAIL) { 881 cc->flag1 |= FLG1_ENCFANFAIL; 882 } else { 883 cc->flag1 &= ~FLG1_ENCFANFAIL; 884 } 885 mutex_exit(&ssc->ses_devp->sd_mutex); 886 err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 887 cc->flag2, 0, slp); 888 if (err) 889 return (err); 890 if (obp->cstat[3] & SESCTL_RQSTON) { 891 uchar_t fsp; 892 if ((obp->cstat[3] & 0x7) == 7) { 893 fsp = 4; 894 } else if ((obp->cstat[3] & 0x7) == 6) { 895 fsp = 3; 896 } else if ((obp->cstat[3] & 0x7) == 4) { 897 fsp = 2; 898 } else { 899 fsp = 1; 900 } 901 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp); 902 } else { 903 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); 904 } 905 break; 906 case SESTYP_DOORLOCK: 907 mutex_enter(&ssc->ses_devp->sd_mutex); 908 if (obp->cstat[3] & 0x1) { 909 cc->flag2 &= ~FLG2_LOCKDOOR; 910 } else { 911 cc->flag2 |= FLG2_LOCKDOOR; 912 } 913 mutex_exit(&ssc->ses_devp->sd_mutex); 914 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 915 cc->flag2, 0, slp); 916 break; 917 case SESTYP_ALARM: 918 /* 919 * On all nonzero but the 'muted' bit, we turn on the alarm, 920 */ 921 mutex_enter(&ssc->ses_devp->sd_mutex); 922 obp->cstat[3] &= ~0xa; 923 if (obp->cstat[3] & 0x40) { 924 cc->flag2 &= ~FLG1_ALARM; 925 } else if (obp->cstat[3] != 0) { 926 cc->flag2 |= FLG1_ALARM; 927 } else { 928 cc->flag2 &= ~FLG1_ALARM; 929 } 930 ep->priv = obp->cstat[3]; 931 mutex_exit(&ssc->ses_devp->sd_mutex); 932 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 933 cc->flag2, 0, slp); 934 break; 935 default: 936 break; 937 } 938 mutex_enter(&ssc->ses_devp->sd_mutex); 939 ep->svalid = 0; 940 mutex_exit(&ssc->ses_devp->sd_mutex); 941 return (0); 942 } 943 944 static int 945 set_objstat_sel(ses_softc_t *ssc, ses_objarg *obp, int slp) 946 { 947 int idx; 948 encobj *ep; 949 struct scfg *cc = ssc->ses_private; 950 951 if (cc == NULL) 952 return (0); 953 954 idx = (int)obp->obj_id; 955 ep = &ssc->ses_objmap[idx]; 956 957 switch (ep->enctype) { 958 case SESTYP_DEVICE: 959 mutex_enter(&ssc->ses_devp->sd_mutex); 960 if (obp->cstat[0] & SESCTL_PRDFAIL) { 961 ep->priv |= 0x40; 962 } 963 /* SESCTL_RSTSWAP has no correspondence in SAF-TE */ 964 if (obp->cstat[0] & SESCTL_DISABLE) { 965 ep->priv |= 0x80; 966 /* 967 * Hmm. Try to set the 'No Drive' flag. 968 * Maybe that will count as a 'disable'. 969 */ 970 } 971 if (ep->priv & 0xc6) { 972 ep->priv &= ~0x1; 973 } else { 974 ep->priv |= 0x1; /* no errors */ 975 } 976 mutex_exit(&ssc->ses_devp->sd_mutex); 977 wrslot_stat(ssc, slp); 978 break; 979 case SESTYP_POWER: 980 /* 981 * Okay- the only one that makes sense here is to 982 * do the 'disable' for a power supply. 983 */ 984 if (obp->cstat[0] & SESCTL_DISABLE) { 985 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS, 986 idx - cc->pwroff, 0, 0, slp); 987 } 988 break; 989 case SESTYP_FAN: 990 /* 991 * Okay- the only one that makes sense here is to 992 * set fan speed to zero on disable. 993 */ 994 if (obp->cstat[0] & SESCTL_DISABLE) { 995 /* remember- fans are the first items, so idx works */ 996 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp); 997 } 998 break; 999 case SESTYP_DOORLOCK: 1000 /* 1001 * Well, we can 'disable' the lock. 1002 */ 1003 if (obp->cstat[0] & SESCTL_DISABLE) { 1004 mutex_enter(&ssc->ses_devp->sd_mutex); 1005 cc->flag2 &= ~FLG2_LOCKDOOR; 1006 mutex_exit(&ssc->ses_devp->sd_mutex); 1007 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 1008 cc->flag2, 0, slp); 1009 } 1010 break; 1011 case SESTYP_ALARM: 1012 /* 1013 * Well, we can 'disable' the alarm. 1014 */ 1015 if (obp->cstat[0] & SESCTL_DISABLE) { 1016 mutex_enter(&ssc->ses_devp->sd_mutex); 1017 cc->flag2 &= ~FLG1_ALARM; 1018 ep->priv |= 0x40; /* Muted */ 1019 mutex_exit(&ssc->ses_devp->sd_mutex); 1020 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, 1021 cc->flag2, 0, slp); 1022 } 1023 break; 1024 default: 1025 break; 1026 } 1027 mutex_enter(&ssc->ses_devp->sd_mutex); 1028 ep->svalid = 0; 1029 mutex_exit(&ssc->ses_devp->sd_mutex); 1030 return (0); 1031 } 1032 1033 /* 1034 * This function handles all of the 16 byte WRITE BUFFER commands. 1035 */ 1036 static int 1037 wrbuf16(ses_softc_t *ssc, uchar_t op, uchar_t b1, uchar_t b2, 1038 uchar_t b3, int slp) 1039 { 1040 int err; 1041 Uscmd local, *lp = &local; 1042 char rqbuf[SENSE_LENGTH], *sdata; 1043 struct scfg *cc = ssc->ses_private; 1044 static char cdb[CDB_GROUP1] = 1045 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 }; 1046 1047 if (cc == NULL) 1048 return (0); 1049 1050 sdata = kmem_alloc(16, slp); 1051 if (sdata == NULL) 1052 return (ENOMEM); 1053 1054 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE; 1055 lp->uscsi_timeout = ses_io_time; 1056 lp->uscsi_cdb = cdb; 1057 lp->uscsi_bufaddr = sdata; 1058 lp->uscsi_buflen = SCRATCH; 1059 lp->uscsi_cdblen = sizeof (cdb); 1060 lp->uscsi_rqbuf = rqbuf; 1061 lp->uscsi_rqlen = sizeof (rqbuf); 1062 1063 sdata[0] = op; 1064 sdata[1] = b1; 1065 sdata[2] = b2; 1066 sdata[3] = b3; 1067 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrbuf16 %x %x %x %x", op, b1, b2, b3); 1068 bzero(&sdata[4], 12); 1069 err = ses_runcmd(ssc, lp); 1070 kmem_free(sdata, 16); 1071 return (err); 1072 } 1073 1074 /* 1075 * This function updates the status byte for the device slot described. 1076 * 1077 * Since this is an optional SAF-TE command, there's no point in 1078 * returning an error. 1079 */ 1080 static void 1081 wrslot_stat(ses_softc_t *ssc, int slp) 1082 { 1083 int i; 1084 encobj *ep; 1085 Uscmd local, *lp = &local; 1086 char rqbuf[SENSE_LENGTH], cdb[CDB_GROUP1], *sdata; 1087 struct scfg *cc = ssc->ses_private; 1088 1089 if (cc == NULL) 1090 return; 1091 1092 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrslot"); 1093 cdb[0] = SCMD_WRITE_BUFFER; 1094 cdb[1] = 1; 1095 cdb[2] = 0; 1096 cdb[3] = 0; 1097 cdb[4] = 0; 1098 cdb[5] = 0; 1099 cdb[6] = 0; 1100 cdb[7] = 0; 1101 cdb[8] = cc->Nslots * 3 + 1; 1102 cdb[9] = 0; 1103 1104 sdata = kmem_zalloc(cc->Nslots * 3 + 1, slp); 1105 if (sdata == NULL) 1106 return; 1107 1108 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE; 1109 lp->uscsi_timeout = ses_io_time; 1110 lp->uscsi_cdb = cdb; 1111 lp->uscsi_bufaddr = sdata; 1112 lp->uscsi_buflen = cc->Nslots * 3 + 1; 1113 lp->uscsi_cdblen = sizeof (cdb); 1114 lp->uscsi_rqbuf = rqbuf; 1115 lp->uscsi_rqlen = sizeof (rqbuf); 1116 1117 sdata[0] = SAFTE_WT_DSTAT; 1118 for (i = 0; i < cc->Nslots; i++) { 1119 ep = &ssc->ses_objmap[cc->slotoff + i]; 1120 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrslot %d <- %x", i, 1121 ep->priv & 0xff); 1122 sdata[1 + (3 * i)] = ep->priv & 0xff; 1123 } 1124 (void) ses_runcmd(ssc, lp); 1125 kmem_free(sdata, cc->Nslots * 3 + 1); 1126 } 1127 1128 /* 1129 * This function issues the "PERFORM SLOT OPERATION" command. 1130 */ 1131 static int 1132 perf_slotop(ses_softc_t *ssc, uchar_t slot, uchar_t opflag, int slp) 1133 { 1134 int err; 1135 Uscmd local, *lp = &local; 1136 char rqbuf[SENSE_LENGTH], *sdata; 1137 struct scfg *cc = ssc->ses_private; 1138 static char cdb[CDB_GROUP1] = 1139 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SCRATCH, 0 }; 1140 1141 if (cc == NULL) 1142 return (0); 1143 1144 sdata = kmem_zalloc(SCRATCH, slp); 1145 if (sdata == NULL) 1146 return (ENOMEM); 1147 1148 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE; 1149 lp->uscsi_timeout = ses_io_time; 1150 lp->uscsi_cdb = cdb; 1151 lp->uscsi_bufaddr = sdata; 1152 lp->uscsi_buflen = SCRATCH; 1153 lp->uscsi_cdblen = sizeof (cdb); 1154 lp->uscsi_rqbuf = rqbuf; 1155 lp->uscsi_rqlen = sizeof (rqbuf); 1156 1157 sdata[0] = SAFTE_WT_SLTOP; 1158 sdata[1] = slot; 1159 sdata[2] = opflag; 1160 SES_LOG(ssc, SES_CE_DEBUG2, "saf_slotop slot %d op %x", slot, opflag); 1161 err = ses_runcmd(ssc, lp); 1162 kmem_free(sdata, SCRATCH); 1163 return (err); 1164 } 1165 1166 /* 1167 * mode: c 1168 * Local variables: 1169 * c-indent-level: 8 1170 * c-brace-imaginary-offset: 0 1171 * c-brace-offset: -8 1172 * c-argdecl-indent: 8 1173 * c-label-offset: -8 1174 * c-continued-statement-offset: 8 1175 * c-continued-brace-offset: 0 1176 * End: 1177 */ 1178