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