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