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))
_NOTE(DATA_READABLE_WITHOUT_LOCK (scfg::Nfans))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
safte_softc_init(ses_softc_t * ssc,int doinit)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
safte_init_enc(ses_softc_t * ssc)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
safte_rdstat(ses_softc_t * ssc,int slpflg)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
safte_get_encstat(ses_softc_t * ssc,int slpflg)737 safte_get_encstat(ses_softc_t *ssc, int slpflg)
738 {
739 return (safte_rdstat(ssc, slpflg));
740 }
741
742 int
safte_set_encstat(ses_softc_t * ssc,uchar_t encstat,int slpflg)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
safte_get_objstat(ses_softc_t * ssc,ses_objarg * obp,int slpflg)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
safte_set_objstat(ses_softc_t * ssc,ses_objarg * obp,int slp)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
set_objstat_sel(ses_softc_t * ssc,ses_objarg * obp,int slp)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
wrbuf16(ses_softc_t * ssc,uchar_t op,uchar_t b1,uchar_t b2,uchar_t b3,int slp)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
wrslot_stat(ses_softc_t * ssc,int slp)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
perf_slotop(ses_softc_t * ssc,uchar_t slot,uchar_t opflag,int slp)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