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