xref: /illumos-gate/usr/src/uts/common/io/scsi/targets/ses_sen.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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
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
111 sen_init_enc(ses_softc_t *ssc)
112 {
113 	UNUSED_PARAMETER(ssc);
114 	return (0);
115 }
116 
117 static int
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
290 sen_get_encstat(ses_softc_t *ssc, int slpflag)
291 {
292 	return (sen_rdstat(ssc, slpflag));
293 }
294 
295 int
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
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
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