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