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