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