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, SEN Enclosure Routines
24 *
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 */
28 #pragma ident "%Z%%M% %I% %E% SMI"
29
30 #include <sys/modctl.h>
31 #include <sys/file.h>
32 #include <sys/scsi/scsi.h>
33 #include <sys/stat.h>
34 #include <sys/scsi/targets/sesio.h>
35 #include <sys/scsi/targets/ses.h>
36
37
38 /*
39 * The SEN unit is wired to support 7 disk units,
40 * two power supplies, one fan module, one overtemp sensor,
41 * and one alarm.
42 */
43 #define NOBJECTS (7+2+1+1+1)
44 #define DRVOFF 0
45 #define SDRVOFF 20
46 #define NDRV 7
47
48 #define PWROFF NDRV
49 #define SPWROFF 28
50 #define NPWR 2
51
52 #define FANOFF (PWROFF + NPWR)
53 #define SFANOFF 30
54 #define NFAN 1
55
56 #define THMOFF (FANOFF + NFAN)
57 #define STHMOFF 31
58 #define NTHM 1
59
60 #define ALRMOFF (THMOFF + NTHM)
61 #define NALRM 1
62 #define SALRMOFF 8
63
64 #define SENPGINSIZE 32
65 #define SENPGOUTSIZE 22
66
67 int
sen_softc_init(ses_softc_t * ssc,int doinit)68 sen_softc_init(ses_softc_t *ssc, int doinit)
69 {
70 int i;
71 if (doinit == 0) {
72 mutex_enter(&ssc->ses_devp->sd_mutex);
73 if (ssc->ses_nobjects) {
74 kmem_free(ssc->ses_objmap,
75 ssc->ses_nobjects * sizeof (encobj));
76 ssc->ses_objmap = NULL;
77 ssc->ses_nobjects = 0;
78 }
79 mutex_exit(&ssc->ses_devp->sd_mutex);
80 return (0);
81 }
82 mutex_enter(&ssc->ses_devp->sd_mutex);
83 ssc->ses_nobjects = 0;
84 ssc->ses_encstat = 0;
85 ssc->ses_objmap = (encobj *)
86 kmem_zalloc(NOBJECTS * sizeof (encobj), KM_SLEEP);
87 if (ssc->ses_objmap == NULL) {
88 mutex_exit(&ssc->ses_devp->sd_mutex);
89 return (ENOMEM);
90 }
91 for (i = DRVOFF; i < DRVOFF + NDRV; i++) {
92 ssc->ses_objmap[i].enctype = SESTYP_DEVICE;
93 }
94 for (i = PWROFF; i < PWROFF + NPWR; i++) {
95 ssc->ses_objmap[i].enctype = SESTYP_POWER;
96 }
97 for (i = FANOFF; i < FANOFF + NFAN; i++) {
98 ssc->ses_objmap[i].enctype = SESTYP_FAN;
99 }
100 for (i = THMOFF; i < THMOFF + NTHM; i++) {
101 ssc->ses_objmap[i].enctype = SESTYP_THERM;
102 }
103 for (i = ALRMOFF; i < ALRMOFF + NALRM; i++) {
104 ssc->ses_objmap[i].enctype = SESTYP_ALARM;
105 }
106 ssc->ses_nobjects = NOBJECTS;
107 mutex_exit(&ssc->ses_devp->sd_mutex);
108 return (0);
109 }
110
111 int
sen_init_enc(ses_softc_t * ssc)112 sen_init_enc(ses_softc_t *ssc)
113 {
114 UNUSED_PARAMETER(ssc);
115 return (0);
116 }
117
118 static int
sen_rdstat(ses_softc_t * ssc,int slpflag)119 sen_rdstat(ses_softc_t *ssc, int slpflag)
120 {
121 int err, i, oid, baseid, tmp;
122 Uscmd local, *lp = &local;
123 char rqbuf[SENSE_LENGTH], *sdata;
124 static char cdb[CDB_GROUP0] =
125 { SCMD_GDIAG, 0x10, 0x4, 0, SENPGINSIZE, 0 };
126
127 /*
128 * Fetch current data
129 */
130 sdata = kmem_alloc(SENPGINSIZE, slpflag);
131 if (sdata == NULL)
132 return (ENOMEM);
133
134 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
135 lp->uscsi_timeout = ses_io_time;
136 lp->uscsi_cdb = cdb;
137 lp->uscsi_bufaddr = sdata;
138 lp->uscsi_buflen = SENPGINSIZE;
139 lp->uscsi_cdblen = sizeof (cdb);
140 lp->uscsi_rqbuf = rqbuf;
141 lp->uscsi_rqlen = sizeof (rqbuf);
142 err = ses_runcmd(ssc, lp);
143 if (err) {
144 kmem_free(sdata, SENPGINSIZE);
145 return (err);
146 }
147
148 if ((lp->uscsi_buflen - lp->uscsi_resid) < SENPGINSIZE) {
149 SES_LOG(ssc, CE_NOTE, "sen_rdstat: too little data (%ld)",
150 lp->uscsi_buflen - lp->uscsi_resid);
151 kmem_free(sdata, SENPGINSIZE);
152 return (EIO);
153 }
154
155 /*
156 * Set base SCSI id for drives...
157 */
158 if (sdata[10] & 0x80)
159 baseid = 8;
160 else
161 baseid = 0;
162
163 oid = 0;
164
165 mutex_enter(&ssc->ses_devp->sd_mutex);
166 /*
167 * Invalidate all status bits.
168 */
169 for (i = 0; i < ssc->ses_nobjects; i++)
170 ssc->ses_objmap[i].svalid = 0;
171 ssc->ses_encstat = 0;
172
173 /*
174 * Do Drives...
175 */
176 for (i = SDRVOFF; i < SDRVOFF + NDRV; i++) {
177 ssc->ses_objmap[oid].encstat[1] = baseid + i - SDRVOFF;
178 ssc->ses_objmap[oid].encstat[2] = 0;
179 if (sdata[i] & 0x80) {
180 /*
181 * Drive is present
182 */
183 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
184 } else {
185 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
186 ssc->ses_encstat |= ENCSTAT_INFO;
187 }
188 /*
189 * Is the fault LED lit?
190 */
191 if (sdata[i] & 0x40) {
192 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
193 ssc->ses_objmap[oid].encstat[3] = 0x40;
194 ssc->ses_encstat |= ENCSTAT_CRITICAL;
195 } else {
196 ssc->ses_objmap[oid].encstat[3] = 0x0;
197 }
198 ssc->ses_objmap[oid++].svalid = 1;
199 }
200
201 /*
202 * Do Power Supplies...
203 *
204 * Power supply bad, or not installed cannot be distinguished.
205 * Which one to pick? Let's say 'bad' and make it NONCRITICAL
206 * if only one is bad but CRITICAL if both are bad.
207 */
208 for (tmp = 0, i = SPWROFF; i < SPWROFF + NPWR; i++) {
209 ssc->ses_objmap[oid].encstat[1] = 0;
210 ssc->ses_objmap[oid].encstat[2] = 0;
211 if ((sdata[i] & 0x80) == 0) {
212 /*
213 * Power supply 'ok'...
214 */
215 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
216 tmp++;
217 } else {
218 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
219 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
220 }
221 ssc->ses_objmap[oid++].svalid = 1;
222 }
223 if (tmp == 0) {
224 ssc->ses_encstat |= ENCSTAT_CRITICAL;
225 }
226
227 /*
228 * Do the Fan(s)
229 */
230 for (i = SFANOFF; i < SFANOFF + NFAN; i++) {
231 ssc->ses_objmap[oid].encstat[1] = 0;
232 ssc->ses_objmap[oid].encstat[2] = 0;
233 if (sdata[i] & 0x20) { /* both fans have failed */
234 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
235 ssc->ses_objmap[oid].encstat[3] = 0x40;
236 ssc->ses_encstat |= ENCSTAT_CRITICAL;
237 } else if (sdata[i] & 0x80) { /* one fan has failed */
238 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NONCRIT;
239 ssc->ses_objmap[oid].encstat[3] = 0x41;
240 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
241 } else {
242 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
243 ssc->ses_objmap[oid].encstat[3] = 0x6;
244 }
245 ssc->ses_objmap[oid++].svalid = 1;
246 }
247
248 /*
249 * Do the temperature sensor...
250 */
251 for (i = STHMOFF; i < STHMOFF + NTHM; i++) {
252 ssc->ses_objmap[oid].encstat[1] = 0;
253 if (sdata[i] & 0x80) {
254 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
255 /* ssc->ses_objmap[oid].encstat[2] = 0; */
256 ssc->ses_objmap[oid].encstat[3] = 0x8;
257 ssc->ses_encstat |= ENCSTAT_CRITICAL;
258 } else {
259 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
260 /* ssc->ses_objmap[oid].encstat[2] = 0; */
261 ssc->ses_objmap[oid].encstat[3] = 0;
262 }
263 ssc->ses_objmap[oid++].svalid = 1;
264 }
265
266 /*
267 * and last, but not least, check the state of the alarm.
268 */
269 for (i = SALRMOFF; i < SALRMOFF + NALRM; i++) {
270 ssc->ses_objmap[oid].encstat[1] = 0;
271 ssc->ses_objmap[oid].encstat[2] = 0;
272 if (sdata[i] & 0x80) { /* Alarm is or was sounding */
273 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
274 ssc->ses_objmap[oid].encstat[3] = 0x2;
275 if ((sdata[i] & 0xf))
276 ssc->ses_objmap[oid].encstat[3] |= 0x40;
277 ssc->ses_encstat |= ENCSTAT_CRITICAL;
278 } else {
279 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
280 ssc->ses_objmap[oid].encstat[3] = 0;
281 }
282 ssc->ses_objmap[oid++].svalid = 1;
283 }
284 ssc->ses_encstat |= ENCI_SVALID;
285 mutex_exit(&ssc->ses_devp->sd_mutex);
286 kmem_free(sdata, SENPGINSIZE);
287 return (0);
288 }
289
290 int
sen_get_encstat(ses_softc_t * ssc,int slpflag)291 sen_get_encstat(ses_softc_t *ssc, int slpflag)
292 {
293 return (sen_rdstat(ssc, slpflag));
294 }
295
296 int
sen_set_encstat(ses_softc_t * ssc,uchar_t encstat,int slpflag)297 sen_set_encstat(ses_softc_t *ssc, uchar_t encstat, int slpflag)
298 {
299 UNUSED_PARAMETER(ssc);
300 UNUSED_PARAMETER(encstat);
301 UNUSED_PARAMETER(slpflag);
302 return (0);
303 }
304
305 int
sen_get_objstat(ses_softc_t * ssc,ses_objarg * obp,int slpflag)306 sen_get_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflag)
307 {
308 int i = (int)obp->obj_id;
309
310 if ((ssc->ses_encstat & ENCI_SVALID) == 0 ||
311 (ssc->ses_objmap[i].svalid) == 0) {
312 int r = sen_rdstat(ssc, slpflag);
313 if (r)
314 return (r);
315 }
316 obp->cstat[0] = ssc->ses_objmap[i].encstat[0];
317 obp->cstat[1] = ssc->ses_objmap[i].encstat[1];
318 obp->cstat[2] = ssc->ses_objmap[i].encstat[2];
319 obp->cstat[3] = ssc->ses_objmap[i].encstat[3];
320 return (0);
321 }
322
323
324 int
sen_set_objstat(ses_softc_t * ssc,ses_objarg * obp,int slpflag)325 sen_set_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflag)
326 {
327 encobj *ep;
328 int err, runcmd, idx;
329 Uscmd local, *lp = &local;
330 char rqbuf[SENSE_LENGTH], *sdata;
331 static char cdb[CDB_GROUP0] =
332 { SCMD_GDIAG, 0x10, 0x4, 0, SENPGINSIZE, 0 };
333 static char cdb1[CDB_GROUP0] =
334 { SCMD_SDIAG, 0x10, 0, 0, SENPGOUTSIZE, 0 };
335
336 /*
337 * If this is clear, we don't do diddly.
338 */
339 if ((obp->cstat[0] & SESCTL_CSEL) == 0) {
340 return (0);
341 }
342 /*
343 * Fetch current data
344 */
345 sdata = kmem_alloc(SENPGINSIZE, slpflag);
346 if (sdata == NULL)
347 return (ENOMEM);
348 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
349 lp->uscsi_timeout = ses_io_time;
350 lp->uscsi_cdb = cdb;
351 lp->uscsi_bufaddr = sdata;
352 lp->uscsi_buflen = SENPGINSIZE;
353 lp->uscsi_cdblen = sizeof (cdb);
354 lp->uscsi_rqbuf = rqbuf;
355 lp->uscsi_rqlen = sizeof (rqbuf);
356 err = ses_runcmd(ssc, lp);
357 if (err) {
358 kmem_free(sdata, SENPGINSIZE);
359 return (err);
360 }
361 if ((lp->uscsi_buflen - lp->uscsi_resid) < SENPGINSIZE) {
362 SES_LOG(ssc, CE_NOTE, "Too Little Data Returned (%ld)",
363 lp->uscsi_buflen - lp->uscsi_resid);
364 kmem_free(sdata, SENPGINSIZE);
365 return (EIO);
366 }
367 /*
368 * Okay, now convert the input page to the output page.
369 */
370 sdata[1] = 0;
371 sdata[3] = 0x12;
372 sdata[6] = 1;
373 sdata[8] &= ~0x80;
374 sdata[10] = 0;
375 sdata[14] = sdata[20] & ~0x80;
376 sdata[15] = sdata[21] & ~0x80;
377 sdata[16] = sdata[22] & ~0x80;
378 sdata[17] = sdata[23] & ~0x80;
379 sdata[18] = sdata[24] & ~0x80;
380 sdata[19] = sdata[25] & ~0x80;
381 sdata[20] = sdata[26] & ~0x80;
382 sdata[21] = 0;
383
384 runcmd = 0;
385
386 idx = (int)obp->obj_id;
387 ep = &ssc->ses_objmap[idx];
388 switch (ep->enctype) {
389 case SESTYP_DEVICE:
390 if (idx < 0 || idx >= NDRV) {
391 err = EINVAL;
392 } else if ((obp->cstat[3] & SESCTL_RQSFLT) != 0) {
393 SES_LOG(ssc, SES_CE_DEBUG1, "faulted %d", idx);
394 sdata[14 + idx] |= 0x40;
395 runcmd++;
396 } else {
397 SES_LOG(ssc, SES_CE_DEBUG1, "clrd fault on %d", idx);
398 sdata[14 + idx] &= ~0x40;
399 runcmd++;
400 }
401 break;
402 case SESTYP_POWER:
403 if ((obp->cstat[3] & SESCTL_RQSTFAIL) ||
404 (obp->cstat[0] & SESCTL_DISABLE)) {
405 SES_LOG(ssc, CE_WARN, "Commanding Off Power Supply!");
406 sdata[10] |= 0x40; /* Seppuku!!!! */
407 runcmd++;
408 }
409 break;
410 case SESTYP_ALARM:
411 /*
412 * On all nonzero but the 'muted' bit,
413 * we turn on the alarm,
414 */
415 obp->cstat[3] &= ~0xa;
416 if ((obp->cstat[3] & 0x40) ||
417 (obp->cstat[0] & SESCTL_DISABLE)) {
418 sdata[8] = 0;
419 } else if (obp->cstat[3] != 0) {
420 sdata[8] = 0x40;
421 } else {
422 sdata[8] = 0;
423 }
424 runcmd++;
425 SES_LOG(ssc, SES_CE_DEBUG1, "%sabling alarm",
426 (sdata[8] & 0x40)? "en" : "dis");
427 break;
428 default:
429 break;
430 }
431
432 if (runcmd) {
433 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
434 lp->uscsi_timeout = ses_io_time;
435 lp->uscsi_cdb = cdb1;
436 lp->uscsi_bufaddr = sdata;
437 lp->uscsi_buflen = SENPGOUTSIZE;
438 lp->uscsi_cdblen = sizeof (cdb);
439 lp->uscsi_rqbuf = rqbuf;
440 lp->uscsi_rqlen = sizeof (rqbuf);
441 err = ses_runcmd(ssc, lp);
442 /* preserve error across the rest of the action */
443 } else {
444 err = 0;
445 }
446
447 mutex_enter(&ssc->ses_devp->sd_mutex);
448 ep->svalid = 0;
449 mutex_exit(&ssc->ses_devp->sd_mutex);
450 kmem_free(sdata, SENPGINSIZE);
451 return (err);
452 }
453 /*
454 * mode: c
455 * Local variables:
456 * c-indent-level: 8
457 * c-brace-imaginary-offset: 0
458 * c-brace-offset: -8
459 * c-argdecl-indent: 8
460 * c-label-offset: -8
461 * c-continued-statement-offset: 8
462 * c-continued-brace-offset: 0
463 * End:
464 */
465