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  */