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