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