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