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, SAF-TE 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 static int set_objstat_sel(ses_softc_t *, ses_objarg *, int);
38 static int wrbuf16(ses_softc_t *, uchar_t, uchar_t, uchar_t, uchar_t, int);
39 static void wrslot_stat(ses_softc_t *, int);
40 static int perf_slotop(ses_softc_t *, uchar_t, uchar_t, int);
41
42 #define ALL_ENC_STAT \
43 (ENCSTAT_CRITICAL|ENCSTAT_UNRECOV|ENCSTAT_NONCRITICAL|ENCSTAT_INFO)
44
45 #define SCRATCH 64
46 #define NPSEUDO_THERM 1
47 #define NPSEUDO_ALARM 1
48 struct scfg {
49 /*
50 * Cached Configuration
51 */
52 uchar_t Nfans; /* Number of Fans */
53 uchar_t Npwr; /* Number of Power Supplies */
54 uchar_t Nslots; /* Number of Device Slots */
55 uchar_t DoorLock; /* Door Lock Installed */
56 uchar_t Ntherm; /* Number of Temperature Sensors */
57 uchar_t Nspkrs; /* Number of Speakers */
58 uchar_t Nalarm; /* Number of Alarms (at least one) */
59 /*
60 * Cached Flag Bytes for Global Status
61 */
62 uchar_t flag1;
63 uchar_t flag2;
64 /*
65 * What object index ID is where various slots start.
66 */
67 uchar_t pwroff;
68 uchar_t slotoff;
69 #define ALARM_OFFSET(cc) (cc)->slotoff - 1
70 };
71 #define FLG1_ALARM 0x1
72 #define FLG1_GLOBFAIL 0x2
73 #define FLG1_GLOBWARN 0x4
74 #define FLG1_ENCPWROFF 0x8
75 #define FLG1_ENCFANFAIL 0x10
76 #define FLG1_ENCPWRFAIL 0x20
77 #define FLG1_ENCDRVFAIL 0x40
78 #define FLG1_ENCDRVWARN 0x80
79
80 #define FLG2_LOCKDOOR 0x4
81 #define SAFTE_PRIVATE sizeof (struct scfg)
82
83 #if !defined(lint)
84 _NOTE(MUTEX_PROTECTS_DATA(scsi_device::sd_mutex, scfg))
85 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nfans))
86 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Npwr))
87 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nslots))
88 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::DoorLock))
89 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Ntherm))
90 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nspkrs))
91 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::Nalarm))
92 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::flag1))
93 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::flag2))
94 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::pwroff))
95 _NOTE(DATA_READABLE_WITHOUT_LOCK(scfg::slotoff))
96 #endif
97
98 static int
99 safte_getconfig(ses_softc_t *ssc)
100 {
101 struct scfg *cfg;
102 int err;
103 Uscmd local, *lp = &local;
104 char rqbuf[SENSE_LENGTH], *sdata;
105 static char cdb[CDB_GROUP1] =
106 { SCMD_READ_BUFFER, 1, SAFTE_RD_RDCFG, 0, 0, 0, 0, 0, SCRATCH, 0 };
107
108 cfg = ssc->ses_private;
109 if (cfg == NULL)
110 return (ENXIO);
111
112 sdata = kmem_alloc(SCRATCH, KM_SLEEP);
113
114 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
115 lp->uscsi_timeout = ses_io_time;
116 lp->uscsi_cdb = cdb;
117 lp->uscsi_bufaddr = sdata;
118 lp->uscsi_buflen = SCRATCH;
119 lp->uscsi_cdblen = sizeof (cdb);
120 lp->uscsi_rqbuf = rqbuf;
121 lp->uscsi_rqlen = sizeof (rqbuf);
122
123 err = ses_runcmd(ssc, lp);
124 if (err) {
125 kmem_free(sdata, SCRATCH);
126 return (err);
127 }
128
129 if ((lp->uscsi_buflen - lp->uscsi_resid) < 6) {
130 SES_LOG(ssc, CE_NOTE, "Too little data (%ld) for configuration",
131 lp->uscsi_buflen - lp->uscsi_resid);
132 kmem_free(sdata, SCRATCH);
133 return (EIO);
134 }
135 SES_LOG(ssc, SES_CE_DEBUG1, "Nfans %d Npwr %d Nslots %d Lck %d Ntherm "
136 "%d Nspkrs %d", sdata[0], sdata[1], sdata[2], sdata[3], sdata[4],
137 sdata[5]);
138
139 mutex_enter(&ssc->ses_devp->sd_mutex);
140 cfg->Nfans = sdata[0];
141 cfg->Npwr = sdata[1];
142 cfg->Nslots = sdata[2];
143 cfg->DoorLock = sdata[3];
144 cfg->Ntherm = sdata[4];
145 cfg->Nspkrs = sdata[5];
146 cfg->Nalarm = NPSEUDO_ALARM;
147 mutex_exit(&ssc->ses_devp->sd_mutex);
148 kmem_free(sdata, SCRATCH);
149 return (0);
150 }
151
152 int
153 safte_softc_init(ses_softc_t *ssc, int doinit)
154 {
155 int r, i;
156 struct scfg *cc;
157
158 if (doinit == 0) {
159 mutex_enter(&ssc->ses_devp->sd_mutex);
160 if (ssc->ses_nobjects) {
161 if (ssc->ses_objmap) {
162 kmem_free(ssc->ses_objmap,
163 ssc->ses_nobjects * sizeof (encobj));
164 ssc->ses_objmap = NULL;
165 }
166 ssc->ses_nobjects = 0;
167 }
168 if (ssc->ses_private) {
169 kmem_free(ssc->ses_private, SAFTE_PRIVATE);
170 ssc->ses_private = NULL;
171 }
172 mutex_exit(&ssc->ses_devp->sd_mutex);
173 return (0);
174 }
175
176 mutex_enter(&ssc->ses_devp->sd_mutex);
177 if (ssc->ses_private == NULL) {
178 ssc->ses_private = kmem_zalloc(SAFTE_PRIVATE, KM_SLEEP);
179 }
180
181 ssc->ses_nobjects = 0;
182 ssc->ses_encstat = 0;
183 mutex_exit(&ssc->ses_devp->sd_mutex);
184
185 if ((r = safte_getconfig(ssc)) != 0) {
186 return (r);
187 }
188
189 /*
190 * The number of objects here, as well as that reported by the
191 * READ_BUFFER/GET_CONFIG call, are the over-temperature flags (15)
192 * that get reported during READ_BUFFER/READ_ENC_STATUS.
193 */
194 mutex_enter(&ssc->ses_devp->sd_mutex);
195 cc = ssc->ses_private;
196 ssc->ses_nobjects = cc->Nfans + cc->Npwr + cc->Nslots + cc->DoorLock +
197 cc->Ntherm + cc->Nspkrs + NPSEUDO_THERM + NPSEUDO_ALARM;
198 ssc->ses_objmap = (encobj *)
199 kmem_zalloc(ssc->ses_nobjects * sizeof (encobj), KM_SLEEP);
200 mutex_exit(&ssc->ses_devp->sd_mutex);
201 if (ssc->ses_objmap == NULL)
202 return (ENOMEM);
203 r = 0;
204 /*
205 * Note that this is all arranged for the convenience
206 * in later fetches of status.
207 */
208 mutex_enter(&ssc->ses_devp->sd_mutex);
209 for (i = 0; i < cc->Nfans; i++)
210 ssc->ses_objmap[r++].enctype = SESTYP_FAN;
211 cc->pwroff = (uchar_t)r;
212 for (i = 0; i < cc->Npwr; i++)
213 ssc->ses_objmap[r++].enctype = SESTYP_POWER;
214 for (i = 0; i < cc->DoorLock; i++)
215 ssc->ses_objmap[r++].enctype = SESTYP_DOORLOCK;
216 for (i = 0; i < cc->Nspkrs; i++)
217 ssc->ses_objmap[r++].enctype = SESTYP_ALARM;
218 for (i = 0; i < cc->Ntherm; i++)
219 ssc->ses_objmap[r++].enctype = SESTYP_THERM;
220 for (i = 0; i < NPSEUDO_THERM; i++)
221 ssc->ses_objmap[r++].enctype = SESTYP_THERM;
222 ssc->ses_objmap[r++].enctype = SESTYP_ALARM;
223 cc->slotoff = (uchar_t)r;
224 for (i = 0; i < cc->Nslots; i++)
225 ssc->ses_objmap[r++].enctype = SESTYP_DEVICE;
226 mutex_exit(&ssc->ses_devp->sd_mutex);
227 return (0);
228 }
229
230 int
231 safte_init_enc(ses_softc_t *ssc)
232 {
233 int err;
234 Uscmd local, *lp = &local;
235 char rqbuf[SENSE_LENGTH], *sdata;
236 static char cdb0[CDB_GROUP1] = { SCMD_SDIAG };
237 static char cdb[CDB_GROUP1] =
238 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SCRATCH, 0 };
239
240 sdata = kmem_alloc(SCRATCH, KM_SLEEP);
241 lp->uscsi_flags = USCSI_RQENABLE;
242 lp->uscsi_timeout = ses_io_time;
243 lp->uscsi_cdb = cdb0;
244 lp->uscsi_bufaddr = NULL;
245 lp->uscsi_buflen = 0;
246 lp->uscsi_cdblen = sizeof (cdb0);
247 lp->uscsi_rqbuf = rqbuf;
248 lp->uscsi_rqlen = sizeof (rqbuf);
249 err = ses_runcmd(ssc, lp);
250 if (err) {
251 kmem_free(sdata, SCRATCH);
252 return (err);
253 }
254
255 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
256 lp->uscsi_timeout = ses_io_time;
257 lp->uscsi_cdb = cdb;
258 lp->uscsi_bufaddr = sdata;
259 lp->uscsi_buflen = SCRATCH;
260 lp->uscsi_cdblen = sizeof (cdb);
261 lp->uscsi_rqbuf = rqbuf;
262 lp->uscsi_rqlen = sizeof (rqbuf);
263 bzero(&sdata[1], 15);
264 sdata[0] = SAFTE_WT_GLOBAL;
265 err = ses_runcmd(ssc, lp);
266 kmem_free(sdata, SCRATCH);
267 return (err);
268 }
269
270
271 static char *toolittle = "Too Little Data Returned (%d) at line %d";
272 #define BAIL(r, x, k, l, m, n) \
273 if (r >= x) { \
274 SES_LOG(ssc, CE_NOTE, toolittle, x, __LINE__); \
275 kmem_free(k, l); \
276 kmem_free(m, n); \
277 return (EIO); \
278 }
279
280 static int
281 safte_rdstat(ses_softc_t *ssc, int slpflg)
282 {
283 int err, oid, r, i, hiwater, nitems;
284 ushort_t tempflags;
285 size_t buflen;
286 uchar_t status, oencstat;
287 Uscmd local, *lp = &local;
288 struct scfg *cc = ssc->ses_private;
289 char rqbuf[SENSE_LENGTH], *sdata;
290 char cdb[CDB_GROUP1];
291 int *driveids, id_size = cc->Nslots * sizeof (int);
292
293 driveids = kmem_alloc(id_size, slpflg);
294 if (driveids == NULL) {
295 return (ENOMEM);
296 }
297
298 /*
299 * The number of bytes of data we need to get is
300 * Nfans + Npwr + Nslots + Nspkrs + Ntherm + nochoice
301 * (nochoice = 1 doorlock + 1 spkr + 2 pseudo therms + 10 extra)
302 * the extra are simply for good luck.
303 */
304 buflen = cc->Nfans + cc->Npwr + cc->Nslots + cc->Nspkrs;
305 buflen += cc->Ntherm + 14;
306
307 /*
308 * Towards the end of this function this buffer is reused.
309 * Thus we need to make sure that we have allocated enough
310 * memory retrieving buffer 1 & 4.
311 * buffer 1 -> element status & drive id
312 * buffer 4 -> drive status & drive command history.
313 * buffer 4 uses 4 bytes per drive bay.
314 */
315
316 if (buflen < cc->Nslots * 4) {
317 buflen = cc->Nslots * 4;
318 }
319
320 if (ssc->ses_nobjects > buflen)
321 buflen = ssc->ses_nobjects;
322
323 if (buflen > 0xffff) {
324 cmn_err(CE_WARN, "Illogical SCSI data");
325 return (EIO);
326 }
327
328 sdata = kmem_alloc(buflen, slpflg);
329 if (sdata == NULL) {
330 kmem_free(driveids, id_size);
331 return (ENOMEM);
332 }
333
334 cdb[0] = SCMD_READ_BUFFER;
335 cdb[1] = 1;
336 cdb[2] = SAFTE_RD_RDESTS;
337 cdb[3] = 0;
338 cdb[4] = 0;
339 cdb[5] = 0;
340 cdb[6] = 0;
341 cdb[7] = (buflen >> 8) & 0xff;
342 cdb[8] = buflen & 0xff;
343 cdb[9] = 0;
344 lp->uscsi_flags = USCSI_READ|USCSI_RQENABLE;
345 lp->uscsi_timeout = ses_io_time;
346 lp->uscsi_cdb = cdb;
347 lp->uscsi_bufaddr = sdata;
348 lp->uscsi_buflen = buflen;
349 lp->uscsi_cdblen = sizeof (cdb);
350 lp->uscsi_rqbuf = rqbuf;
351 lp->uscsi_rqlen = sizeof (rqbuf);
352
353 err = ses_runcmd(ssc, lp);
354 if (err) {
355 kmem_free(sdata, buflen);
356 kmem_free(driveids, id_size);
357 return (err);
358 }
359
360 hiwater = lp->uscsi_buflen - lp->uscsi_resid;
361
362 /*
363 * invalidate all status bits.
364 */
365 mutex_enter(&ssc->ses_devp->sd_mutex);
366 for (i = 0; i < ssc->ses_nobjects; i++)
367 ssc->ses_objmap[i].svalid = 0;
368 oencstat = ssc->ses_encstat & ALL_ENC_STAT;
369 ssc->ses_encstat = 0;
370 mutex_exit(&ssc->ses_devp->sd_mutex);
371
372 /*
373 * Now parse returned buffer.
374 * If we didn't get enough data back,
375 * that's considered a fatal error.
376 */
377 oid = r = 0;
378
379 for (nitems = i = 0; i < cc->Nfans; i++) {
380 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
381 /*
382 * 0 = Fan Operational
383 * 1 = Fan is malfunctioning
384 * 2 = Fan is not present
385 * 0x80 = Unknown or Not Reportable Status
386 */
387 mutex_enter(&ssc->ses_devp->sd_mutex);
388 ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */
389 ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */
390 switch ((uchar_t)sdata[r]) {
391 case 0:
392 nitems++;
393 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
394 /*
395 * We could get fancier and cache
396 * fan speeds that we have set, but
397 * that isn't done now.
398 */
399 ssc->ses_objmap[oid].encstat[3] = 7;
400 break;
401
402 case 1:
403 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
404 /*
405 * FAIL and FAN STOPPED synthesized
406 */
407 ssc->ses_objmap[oid].encstat[3] = 0x40;
408 /*
409 * Enclosure marked with CRITICAL error
410 * if only one fan or no thermometers,
411 * else NONCRIT error set.
412 */
413 if (cc->Nfans == 1 || cc->Ntherm == 0)
414 ssc->ses_encstat |= ENCSTAT_CRITICAL;
415 else
416 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
417 break;
418 case 2:
419 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
420 ssc->ses_objmap[oid].encstat[3] = 0;
421 if (cc->Nfans == 1)
422 ssc->ses_encstat |= ENCSTAT_CRITICAL;
423 else
424 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
425 break;
426 case 0x80:
427 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN;
428 ssc->ses_objmap[oid].encstat[3] = 0;
429 ssc->ses_encstat |= ENCSTAT_INFO;
430 break;
431 default:
432 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
433 SES_LOG(ssc, CE_NOTE, "unknown fan%d status 0x%x",
434 i, sdata[r] & 0xff);
435 break;
436 }
437 ssc->ses_objmap[oid++].svalid = 1;
438 mutex_exit(&ssc->ses_devp->sd_mutex);
439 r++;
440 }
441 mutex_enter(&ssc->ses_devp->sd_mutex);
442 /*
443 * No matter how you cut it, no cooling elements when there
444 * should be some there is critical.
445 */
446 if (cc->Nfans && nitems == 0) {
447 ssc->ses_encstat |= ENCSTAT_CRITICAL;
448 }
449 mutex_exit(&ssc->ses_devp->sd_mutex);
450
451
452 for (i = 0; i < cc->Npwr; i++) {
453 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
454 mutex_enter(&ssc->ses_devp->sd_mutex);
455 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
456 ssc->ses_objmap[oid].encstat[1] = 0; /* resvd */
457 ssc->ses_objmap[oid].encstat[2] = 0; /* resvd */
458 ssc->ses_objmap[oid].encstat[3] = 0x20; /* requested on */
459 switch ((uchar_t)sdata[r]) {
460 case 0x00: /* pws operational and on */
461 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
462 break;
463 case 0x01: /* pws operational and off */
464 ssc->ses_objmap[oid].encstat[3] = 0x10;
465 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTAVAIL;
466 ssc->ses_encstat |= ENCSTAT_INFO;
467 break;
468 case 0x10: /* pws is malfunctioning and commanded on */
469 ssc->ses_objmap[oid].encstat[3] = 0x61;
470 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
471 if (cc->Npwr < 2)
472 ssc->ses_encstat |= ENCSTAT_CRITICAL;
473 else
474 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
475 break;
476
477 case 0x11: /* pws is malfunctioning and commanded off */
478 ssc->ses_objmap[oid].encstat[3] = 0x51;
479 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
480 if (cc->Npwr < 2)
481 ssc->ses_encstat |= ENCSTAT_CRITICAL;
482 else
483 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
484 break;
485 case 0x20: /* pws is not present */
486 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
487 ssc->ses_objmap[oid].encstat[3] = 0;
488 if (cc->Npwr < 2)
489 ssc->ses_encstat |= ENCSTAT_CRITICAL;
490 else
491 ssc->ses_encstat |= ENCSTAT_INFO;
492 break;
493 case 0x21: /* pws is present */
494 break;
495 case 0x80: /* Unknown or Not Reportable Status */
496 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN;
497 ssc->ses_objmap[oid].encstat[3] = 0;
498 ssc->ses_encstat |= ENCSTAT_INFO;
499 break;
500 default:
501 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
502 SES_LOG(ssc, CE_NOTE, "unknown pwr%d status 0x%x",
503 i, sdata[r] & 0xff);
504 break;
505 }
506 ssc->ses_objmap[oid++].svalid = 1;
507 mutex_exit(&ssc->ses_devp->sd_mutex);
508 r++;
509 }
510
511 /*
512 * Now I am going to save the target id's for the end of
513 * the function. (when I build the drive objects)
514 * that is when I will be getting the drive status from buffer 4
515 */
516
517 for (i = 0; i < cc->Nslots; i++) {
518 driveids[i] = sdata[r++];
519 }
520
521
522
523 /*
524 * We always have doorlock status, no matter what,
525 * but we only save the status if we have one.
526 */
527 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
528 if (cc->DoorLock) {
529 /*
530 * 0 = Door Locked
531 * 1 = Door Unlocked, or no Lock Installed
532 * 0x80 = Unknown or Not Reportable Status
533 */
534 mutex_enter(&ssc->ses_devp->sd_mutex);
535 ssc->ses_objmap[oid].encstat[1] = 0;
536 ssc->ses_objmap[oid].encstat[2] = 0;
537 switch ((uchar_t)sdata[r]) {
538 case 0:
539 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
540 ssc->ses_objmap[oid].encstat[3] = 0;
541 break;
542 case 1:
543 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
544 ssc->ses_objmap[oid].encstat[3] = 1;
545 break;
546 case 0x80:
547 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNKNOWN;
548 ssc->ses_objmap[oid].encstat[3] = 0;
549 ssc->ses_encstat |= ENCSTAT_INFO;
550 break;
551 default:
552 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
553 SES_LOG(ssc, CE_NOTE, "unknown lock status 0x%x",
554 sdata[r] & 0xff);
555 break;
556 }
557 ssc->ses_objmap[oid++].svalid = 1;
558 mutex_exit(&ssc->ses_devp->sd_mutex);
559 }
560 r++;
561
562 /*
563 * We always have speaker status, no matter what,
564 * but we only save the status if we have one.
565 */
566 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
567 if (cc->Nspkrs) {
568 mutex_enter(&ssc->ses_devp->sd_mutex);
569 ssc->ses_objmap[oid].encstat[1] = 0;
570 ssc->ses_objmap[oid].encstat[2] = 0;
571 if (sdata[r] == 1) {
572 /*
573 * We need to cache tone urgency indicators.
574 * Someday.
575 */
576 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NONCRIT;
577 ssc->ses_objmap[oid].encstat[3] = 0x8;
578 ssc->ses_encstat |= ENCSTAT_NONCRITICAL;
579 } else if (sdata[r] == 0) {
580 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
581 ssc->ses_objmap[oid].encstat[3] = 0;
582 } else {
583 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
584 ssc->ses_objmap[oid].encstat[3] = 0;
585 SES_LOG(ssc, CE_NOTE, "unknown spkr status 0x%x",
586 sdata[r] & 0xff);
587 }
588 ssc->ses_objmap[oid++].svalid = 1;
589 mutex_exit(&ssc->ses_devp->sd_mutex);
590 }
591 r++;
592
593 for (i = 0; i < cc->Ntherm; i++) {
594 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
595 /*
596 * Status is a range from -10 to 245 deg Celsius,
597 * which we need to normalize to -20 to -235 according
598 * to the latest SCSI spec.
599 */
600 mutex_enter(&ssc->ses_devp->sd_mutex);
601 ssc->ses_objmap[oid].encstat[1] = 0;
602 ssc->ses_objmap[oid].encstat[2] =
603 ((unsigned int) sdata[r]) - 10;
604 if (sdata[r] < 20) {
605 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
606 /*
607 * Set 'under temperature' failure.
608 */
609 ssc->ses_objmap[oid].encstat[3] = 2;
610 ssc->ses_encstat |= ENCSTAT_CRITICAL;
611 } else if (sdata[r] > 30) {
612 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
613 /*
614 * Set 'over temperature' failure.
615 */
616 ssc->ses_objmap[oid].encstat[3] = 8;
617 ssc->ses_encstat |= ENCSTAT_CRITICAL;
618 } else {
619 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
620 }
621 ssc->ses_objmap[oid++].svalid = 1;
622 mutex_exit(&ssc->ses_devp->sd_mutex);
623 r++;
624 }
625
626 /*
627 * Now, for "pseudo" thermometers, we have two bytes
628 * of information in enclosure status- 16 bits. Actually,
629 * the MSB is a single TEMP ALERT flag indicating whether
630 * any other bits are set, but, thanks to fuzzy thinking,
631 * in the SAF-TE spec, this can also be set even if no
632 * other bits are set, thus making this really another
633 * binary temperature sensor.
634 */
635
636 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
637 tempflags = sdata[r++];
638 BAIL(r, hiwater, sdata, buflen, driveids, id_size);
639 tempflags |= (tempflags << 8) | sdata[r++];
640 mutex_enter(&ssc->ses_devp->sd_mutex);
641
642 #if NPSEUDO_THERM == 1
643 ssc->ses_objmap[oid].encstat[1] = 0;
644 if (tempflags) {
645 /* Set 'over temperature' failure. */
646 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
647 ssc->ses_objmap[oid].encstat[3] = 8;
648 ssc->ses_encstat |= ENCSTAT_CRITICAL;
649 } else {
650 /* Set 'nominal' temperature. */
651 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
652 }
653 ssc->ses_objmap[oid++].svalid = 1;
654
655 #else /* NPSEUDO_THERM == 1 */
656 for (i = 0; i < NPSEUDO_THERM; i++) {
657 ssc->ses_objmap[oid].encstat[1] = 0;
658 if (tempflags & (1 << (NPSEUDO_THERM - i - 1))) {
659 ssc->ses_objmap[oid].encstat[0] = SESSTAT_CRIT;
660 /* ssc->ses_objmap[oid].encstat[2] = 0; */
661
662 /*
663 * Set 'over temperature' failure.
664 */
665 ssc->ses_objmap[oid].encstat[3] = 8;
666 ssc->ses_encstat |= ENCSTAT_CRITICAL;
667 } else {
668 /*
669 * Set 'nominal' temperature.
670 */
671 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
672 /* ssc->ses_objmap[oid].encstat[2] = 0; */
673 }
674 ssc->ses_objmap[oid++].svalid = 1;
675 }
676 #endif /* NPSEUDO_THERM == 1 */
677
678
679 /*
680 * Get alarm status.
681 */
682 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
683 ssc->ses_objmap[oid].encstat[3] = ssc->ses_objmap[oid].priv;
684 ssc->ses_objmap[oid++].svalid = 1;
685 mutex_exit(&ssc->ses_devp->sd_mutex);
686
687 /*
688 * Now get drive slot status
689 */
690 cdb[2] = SAFTE_RD_RDDSTS;
691 err = ses_runcmd(ssc, lp);
692 if (err) {
693 kmem_free(sdata, buflen);
694 kmem_free(driveids, id_size);
695 return (err);
696 }
697 hiwater = lp->uscsi_buflen - lp->uscsi_resid;
698 for (r = i = 0; i < cc->Nslots; i++, r += 4) {
699 BAIL(r+3, hiwater, sdata, buflen, driveids, id_size);
700 mutex_enter(&ssc->ses_devp->sd_mutex);
701 ssc->ses_objmap[oid].encstat[0] = SESSTAT_UNSUPPORTED;
702 ssc->ses_objmap[oid].encstat[1] = (uchar_t)driveids[i];
703 ssc->ses_objmap[oid].encstat[2] = 0;
704 ssc->ses_objmap[oid].encstat[3] = 0;
705 status = sdata[r+3];
706 if ((status & 0x1) == 0) { /* no device */
707 ssc->ses_objmap[oid].encstat[0] = SESSTAT_NOTINSTALLED;
708 } else {
709 ssc->ses_objmap[oid].encstat[0] = SESSTAT_OK;
710 }
711 if (status & 0x2) {
712 ssc->ses_objmap[oid].encstat[2] = 0x8;
713 }
714 if ((status & 0x4) == 0) {
715 ssc->ses_objmap[oid].encstat[3] = 0x10;
716 }
717 ssc->ses_objmap[oid++].svalid = 1;
718 mutex_exit(&ssc->ses_devp->sd_mutex);
719 }
720 mutex_enter(&ssc->ses_devp->sd_mutex);
721 /* see comment below about sticky enclosure status */
722 ssc->ses_encstat |= ENCI_SVALID | oencstat;
723 mutex_exit(&ssc->ses_devp->sd_mutex);
724 kmem_free(sdata, buflen);
725 kmem_free(driveids, id_size);
726 return (0);
727 }
728
729 int
730 safte_get_encstat(ses_softc_t *ssc, int slpflg)
731 {
732 return (safte_rdstat(ssc, slpflg));
733 }
734
735 int
736 safte_set_encstat(ses_softc_t *ssc, uchar_t encstat, int slpflg)
737 {
738 struct scfg *cc = ssc->ses_private;
739 if (cc == NULL)
740 return (0);
741 mutex_enter(&ssc->ses_devp->sd_mutex);
742 /*
743 * Since SAF-TE devices aren't necessarily sticky in terms
744 * of state, make our soft copy of enclosure status 'sticky'-
745 * that is, things set in enclosure status stay set (as implied
746 * by conditions set in reading object status) until cleared.
747 */
748 ssc->ses_encstat &= ~ALL_ENC_STAT;
749 ssc->ses_encstat |= (encstat & ALL_ENC_STAT);
750 ssc->ses_encstat |= ENCI_SVALID;
751 cc->flag1 &= ~(FLG1_ALARM|FLG1_GLOBFAIL|FLG1_GLOBWARN);
752 if ((encstat & (ENCSTAT_CRITICAL|ENCSTAT_UNRECOV)) != 0) {
753 cc->flag1 |= FLG1_ALARM|FLG1_GLOBFAIL;
754 } else if ((encstat & ENCSTAT_NONCRITICAL) != 0) {
755 cc->flag1 |= FLG1_GLOBWARN;
756 }
757 mutex_exit(&ssc->ses_devp->sd_mutex);
758 return (wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1, cc->flag2, 0, slpflg));
759 }
760
761 int
762 safte_get_objstat(ses_softc_t *ssc, ses_objarg *obp, int slpflg)
763 {
764 int i = (int)obp->obj_id;
765
766 if ((ssc->ses_encstat & ENCI_SVALID) == 0 ||
767 (ssc->ses_objmap[i].svalid) == 0) {
768 int r = safte_rdstat(ssc, slpflg);
769 if (r)
770 return (r);
771 }
772 obp->cstat[0] = ssc->ses_objmap[i].encstat[0];
773 obp->cstat[1] = ssc->ses_objmap[i].encstat[1];
774 obp->cstat[2] = ssc->ses_objmap[i].encstat[2];
775 obp->cstat[3] = ssc->ses_objmap[i].encstat[3];
776 return (0);
777 }
778
779
780 int
781 safte_set_objstat(ses_softc_t *ssc, ses_objarg *obp, int slp)
782 {
783 int idx, err;
784 encobj *ep;
785 struct scfg *cc;
786
787
788 SES_LOG(ssc, SES_CE_DEBUG2, "safte_set_objstat(%d): %x %x %x %x",
789 (int)obp->obj_id, obp->cstat[0], obp->cstat[1], obp->cstat[2],
790 obp->cstat[3]);
791
792 /*
793 * If this is clear, we don't do diddly.
794 */
795 if ((obp->cstat[0] & SESCTL_CSEL) == 0) {
796 return (0);
797 }
798
799 err = 0;
800 /*
801 * Check to see if the common bits are set and do them first.
802 */
803 if (obp->cstat[0] & ~SESCTL_CSEL) {
804 err = set_objstat_sel(ssc, obp, slp);
805 if (err)
806 return (err);
807 }
808
809 cc = ssc->ses_private;
810 if (cc == NULL)
811 return (0);
812
813 idx = (int)obp->obj_id;
814 ep = &ssc->ses_objmap[idx];
815
816 switch (ep->enctype) {
817 case SESTYP_DEVICE:
818 {
819 uchar_t slotop = 0;
820 /*
821 * XXX: I should probably cache the previous state
822 * XXX: of SESCTL_DEVOFF so that when it goes from
823 * XXX: true to false I can then set PREPARE FOR OPERATION
824 * XXX: flag in PERFORM SLOT OPERATION write buffer command.
825 */
826 if (obp->cstat[2] & (SESCTL_RQSINS|SESCTL_RQSRMV)) {
827 slotop |= 0x2;
828 }
829 if (obp->cstat[2] & SESCTL_RQSID) {
830 slotop |= 0x4;
831 }
832 err = perf_slotop(ssc, (uchar_t)idx - (uchar_t)cc->slotoff,
833 slotop, slp);
834 if (err)
835 return (err);
836 mutex_enter(&ssc->ses_devp->sd_mutex);
837 if (obp->cstat[3] & SESCTL_RQSFLT) {
838 ep->priv |= 0x2;
839 } else {
840 ep->priv &= ~0x2;
841 }
842 if (ep->priv & 0xc6) {
843 ep->priv &= ~0x1;
844 } else {
845 ep->priv |= 0x1; /* no errors */
846 }
847 mutex_exit(&ssc->ses_devp->sd_mutex);
848 wrslot_stat(ssc, slp);
849 break;
850 }
851 case SESTYP_POWER:
852 mutex_enter(&ssc->ses_devp->sd_mutex);
853 if (obp->cstat[3] & SESCTL_RQSTFAIL) {
854 cc->flag1 |= FLG1_ENCPWRFAIL;
855 } else {
856 cc->flag1 &= ~FLG1_ENCPWRFAIL;
857 }
858 mutex_exit(&ssc->ses_devp->sd_mutex);
859 err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
860 cc->flag2, 0, slp);
861 if (err)
862 return (err);
863 if (obp->cstat[3] & SESCTL_RQSTON) {
864 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS,
865 idx - cc->pwroff, 0, 0, slp);
866 } else {
867 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS,
868 idx - cc->pwroff, 0, 1, slp);
869 }
870 break;
871 case SESTYP_FAN:
872 mutex_enter(&ssc->ses_devp->sd_mutex);
873 if (obp->cstat[3] & SESCTL_RQSTFAIL) {
874 cc->flag1 |= FLG1_ENCFANFAIL;
875 } else {
876 cc->flag1 &= ~FLG1_ENCFANFAIL;
877 }
878 mutex_exit(&ssc->ses_devp->sd_mutex);
879 err = wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
880 cc->flag2, 0, slp);
881 if (err)
882 return (err);
883 if (obp->cstat[3] & SESCTL_RQSTON) {
884 uchar_t fsp;
885 if ((obp->cstat[3] & 0x7) == 7) {
886 fsp = 4;
887 } else if ((obp->cstat[3] & 0x7) == 6) {
888 fsp = 3;
889 } else if ((obp->cstat[3] & 0x7) == 4) {
890 fsp = 2;
891 } else {
892 fsp = 1;
893 }
894 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, fsp, 0, slp);
895 } else {
896 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp);
897 }
898 break;
899 case SESTYP_DOORLOCK:
900 mutex_enter(&ssc->ses_devp->sd_mutex);
901 if (obp->cstat[3] & 0x1) {
902 cc->flag2 &= ~FLG2_LOCKDOOR;
903 } else {
904 cc->flag2 |= FLG2_LOCKDOOR;
905 }
906 mutex_exit(&ssc->ses_devp->sd_mutex);
907 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
908 cc->flag2, 0, slp);
909 break;
910 case SESTYP_ALARM:
911 /*
912 * On all nonzero but the 'muted' bit, we turn on the alarm,
913 */
914 mutex_enter(&ssc->ses_devp->sd_mutex);
915 obp->cstat[3] &= ~0xa;
916 if (obp->cstat[3] & 0x40) {
917 cc->flag2 &= ~FLG1_ALARM;
918 } else if (obp->cstat[3] != 0) {
919 cc->flag2 |= FLG1_ALARM;
920 } else {
921 cc->flag2 &= ~FLG1_ALARM;
922 }
923 ep->priv = obp->cstat[3];
924 mutex_exit(&ssc->ses_devp->sd_mutex);
925 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
926 cc->flag2, 0, slp);
927 break;
928 default:
929 break;
930 }
931 mutex_enter(&ssc->ses_devp->sd_mutex);
932 ep->svalid = 0;
933 mutex_exit(&ssc->ses_devp->sd_mutex);
934 return (0);
935 }
936
937 static int
938 set_objstat_sel(ses_softc_t *ssc, ses_objarg *obp, int slp)
939 {
940 int idx;
941 encobj *ep;
942 struct scfg *cc = ssc->ses_private;
943
944 if (cc == NULL)
945 return (0);
946
947 idx = (int)obp->obj_id;
948 ep = &ssc->ses_objmap[idx];
949
950 switch (ep->enctype) {
951 case SESTYP_DEVICE:
952 mutex_enter(&ssc->ses_devp->sd_mutex);
953 if (obp->cstat[0] & SESCTL_PRDFAIL) {
954 ep->priv |= 0x40;
955 }
956 /* SESCTL_RSTSWAP has no correspondence in SAF-TE */
957 if (obp->cstat[0] & SESCTL_DISABLE) {
958 ep->priv |= 0x80;
959 /*
960 * Hmm. Try to set the 'No Drive' flag.
961 * Maybe that will count as a 'disable'.
962 */
963 }
964 if (ep->priv & 0xc6) {
965 ep->priv &= ~0x1;
966 } else {
967 ep->priv |= 0x1; /* no errors */
968 }
969 mutex_exit(&ssc->ses_devp->sd_mutex);
970 wrslot_stat(ssc, slp);
971 break;
972 case SESTYP_POWER:
973 /*
974 * Okay- the only one that makes sense here is to
975 * do the 'disable' for a power supply.
976 */
977 if (obp->cstat[0] & SESCTL_DISABLE) {
978 (void) wrbuf16(ssc, SAFTE_WT_ACTPWS,
979 idx - cc->pwroff, 0, 0, slp);
980 }
981 break;
982 case SESTYP_FAN:
983 /*
984 * Okay- the only one that makes sense here is to
985 * set fan speed to zero on disable.
986 */
987 if (obp->cstat[0] & SESCTL_DISABLE) {
988 /* remember- fans are the first items, so idx works */
989 (void) wrbuf16(ssc, SAFTE_WT_FANSPD, idx, 0, 0, slp);
990 }
991 break;
992 case SESTYP_DOORLOCK:
993 /*
994 * Well, we can 'disable' the lock.
995 */
996 if (obp->cstat[0] & SESCTL_DISABLE) {
997 mutex_enter(&ssc->ses_devp->sd_mutex);
998 cc->flag2 &= ~FLG2_LOCKDOOR;
999 mutex_exit(&ssc->ses_devp->sd_mutex);
1000 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
1001 cc->flag2, 0, slp);
1002 }
1003 break;
1004 case SESTYP_ALARM:
1005 /*
1006 * Well, we can 'disable' the alarm.
1007 */
1008 if (obp->cstat[0] & SESCTL_DISABLE) {
1009 mutex_enter(&ssc->ses_devp->sd_mutex);
1010 cc->flag2 &= ~FLG1_ALARM;
1011 ep->priv |= 0x40; /* Muted */
1012 mutex_exit(&ssc->ses_devp->sd_mutex);
1013 (void) wrbuf16(ssc, SAFTE_WT_GLOBAL, cc->flag1,
1014 cc->flag2, 0, slp);
1015 }
1016 break;
1017 default:
1018 break;
1019 }
1020 mutex_enter(&ssc->ses_devp->sd_mutex);
1021 ep->svalid = 0;
1022 mutex_exit(&ssc->ses_devp->sd_mutex);
1023 return (0);
1024 }
1025
1026 /*
1027 * This function handles all of the 16 byte WRITE BUFFER commands.
1028 */
1029 static int
1030 wrbuf16(ses_softc_t *ssc, uchar_t op, uchar_t b1, uchar_t b2,
1031 uchar_t b3, int slp)
1032 {
1033 int err;
1034 Uscmd local, *lp = &local;
1035 char rqbuf[SENSE_LENGTH], *sdata;
1036 struct scfg *cc = ssc->ses_private;
1037 static char cdb[CDB_GROUP1] =
1038 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, 16, 0 };
1039
1040 if (cc == NULL)
1041 return (0);
1042
1043 sdata = kmem_alloc(16, slp);
1044 if (sdata == NULL)
1045 return (ENOMEM);
1046
1047 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
1048 lp->uscsi_timeout = ses_io_time;
1049 lp->uscsi_cdb = cdb;
1050 lp->uscsi_bufaddr = sdata;
1051 lp->uscsi_buflen = SCRATCH;
1052 lp->uscsi_cdblen = sizeof (cdb);
1053 lp->uscsi_rqbuf = rqbuf;
1054 lp->uscsi_rqlen = sizeof (rqbuf);
1055
1056 sdata[0] = op;
1057 sdata[1] = b1;
1058 sdata[2] = b2;
1059 sdata[3] = b3;
1060 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrbuf16 %x %x %x %x", op, b1, b2, b3);
1061 bzero(&sdata[4], 12);
1062 err = ses_runcmd(ssc, lp);
1063 kmem_free(sdata, 16);
1064 return (err);
1065 }
1066
1067 /*
1068 * This function updates the status byte for the device slot described.
1069 *
1070 * Since this is an optional SAF-TE command, there's no point in
1071 * returning an error.
1072 */
1073 static void
1074 wrslot_stat(ses_softc_t *ssc, int slp)
1075 {
1076 int i;
1077 encobj *ep;
1078 Uscmd local, *lp = &local;
1079 char rqbuf[SENSE_LENGTH], cdb[CDB_GROUP1], *sdata;
1080 struct scfg *cc = ssc->ses_private;
1081
1082 if (cc == NULL)
1083 return;
1084
1085 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrslot");
1086 cdb[0] = SCMD_WRITE_BUFFER;
1087 cdb[1] = 1;
1088 cdb[2] = 0;
1089 cdb[3] = 0;
1090 cdb[4] = 0;
1091 cdb[5] = 0;
1092 cdb[6] = 0;
1093 cdb[7] = 0;
1094 cdb[8] = cc->Nslots * 3 + 1;
1095 cdb[9] = 0;
1096
1097 sdata = kmem_zalloc(cc->Nslots * 3 + 1, slp);
1098 if (sdata == NULL)
1099 return;
1100
1101 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
1102 lp->uscsi_timeout = ses_io_time;
1103 lp->uscsi_cdb = cdb;
1104 lp->uscsi_bufaddr = sdata;
1105 lp->uscsi_buflen = cc->Nslots * 3 + 1;
1106 lp->uscsi_cdblen = sizeof (cdb);
1107 lp->uscsi_rqbuf = rqbuf;
1108 lp->uscsi_rqlen = sizeof (rqbuf);
1109
1110 sdata[0] = SAFTE_WT_DSTAT;
1111 for (i = 0; i < cc->Nslots; i++) {
1112 ep = &ssc->ses_objmap[cc->slotoff + i];
1113 SES_LOG(ssc, SES_CE_DEBUG2, "saf_wrslot %d <- %x", i,
1114 ep->priv & 0xff);
1115 sdata[1 + (3 * i)] = ep->priv & 0xff;
1116 }
1117 (void) ses_runcmd(ssc, lp);
1118 kmem_free(sdata, cc->Nslots * 3 + 1);
1119 }
1120
1121 /*
1122 * This function issues the "PERFORM SLOT OPERATION" command.
1123 */
1124 static int
1125 perf_slotop(ses_softc_t *ssc, uchar_t slot, uchar_t opflag, int slp)
1126 {
1127 int err;
1128 Uscmd local, *lp = &local;
1129 char rqbuf[SENSE_LENGTH], *sdata;
1130 struct scfg *cc = ssc->ses_private;
1131 static char cdb[CDB_GROUP1] =
1132 { SCMD_WRITE_BUFFER, 1, 0, 0, 0, 0, 0, 0, SCRATCH, 0 };
1133
1134 if (cc == NULL)
1135 return (0);
1136
1137 sdata = kmem_zalloc(SCRATCH, slp);
1138 if (sdata == NULL)
1139 return (ENOMEM);
1140
1141 lp->uscsi_flags = USCSI_WRITE|USCSI_RQENABLE;
1142 lp->uscsi_timeout = ses_io_time;
1143 lp->uscsi_cdb = cdb;
1144 lp->uscsi_bufaddr = sdata;
1145 lp->uscsi_buflen = SCRATCH;
1146 lp->uscsi_cdblen = sizeof (cdb);
1147 lp->uscsi_rqbuf = rqbuf;
1148 lp->uscsi_rqlen = sizeof (rqbuf);
1149
1150 sdata[0] = SAFTE_WT_SLTOP;
1151 sdata[1] = slot;
1152 sdata[2] = opflag;
1153 SES_LOG(ssc, SES_CE_DEBUG2, "saf_slotop slot %d op %x", slot, opflag);
1154 err = ses_runcmd(ssc, lp);
1155 kmem_free(sdata, SCRATCH);
1156 return (err);
1157 }
1158
1159 /*
1160 * mode: c
1161 * Local variables:
1162 * c-indent-level: 8
1163 * c-brace-imaginary-offset: 0
1164 * c-brace-offset: -8
1165 * c-argdecl-indent: 8
1166 * c-label-offset: -8
1167 * c-continued-statement-offset: 8
1168 * c-continued-brace-offset: 0
1169 * End:
1170 */