xref: /openbsd-src/sys/scsi/ses.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: ses.c,v 1.47 2007/09/16 01:30:24 krw Exp $ */
2 
3 /*
4  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "bio.h"
20 
21 #include <sys/param.h>
22 #include <sys/systm.h>
23 #include <sys/device.h>
24 #include <sys/scsiio.h>
25 #include <sys/malloc.h>
26 #include <sys/proc.h>
27 #include <sys/rwlock.h>
28 #include <sys/queue.h>
29 #include <sys/sensors.h>
30 
31 #if NBIO > 0
32 #include <dev/biovar.h>
33 #endif
34 
35 #include <scsi/scsi_all.h>
36 #include <scsi/scsiconf.h>
37 
38 #include <scsi/ses.h>
39 
40 #ifdef SES_DEBUG
41 #define DPRINTF(x...)		do { if (sesdebug) printf(x); } while (0)
42 #define DPRINTFN(n, x...)	do { if (sesdebug > (n)) printf(x); } while (0)
43 int	sesdebug = 2;
44 #else
45 #define DPRINTF(x...)		/* x */
46 #define DPRINTFN(n,x...)	/* n: x */
47 #endif
48 
49 int	ses_match(struct device *, void *, void *);
50 void	ses_attach(struct device *, struct device *, void *);
51 int	ses_detach(struct device *, int);
52 
53 struct ses_sensor {
54 	struct ksensor		se_sensor;
55 	u_int8_t		se_type;
56 	struct ses_status	*se_stat;
57 
58 	TAILQ_ENTRY(ses_sensor)	se_entry;
59 };
60 
61 #if NBIO > 0
62 struct ses_slot {
63 	struct ses_status	*sl_stat;
64 
65 	TAILQ_ENTRY(ses_slot)	sl_entry;
66 };
67 #endif
68 
69 struct ses_softc {
70 	struct device		sc_dev;
71 	struct scsi_link	*sc_link;
72 	struct rwlock		sc_lock;
73 
74 	enum {
75 		SES_ENC_STD,
76 		SES_ENC_DELL
77 	}			sc_enctype;
78 
79 	u_char			*sc_buf;
80 	ssize_t			sc_buflen;
81 
82 #if NBIO > 0
83 	TAILQ_HEAD(, ses_slot)	sc_slots;
84 #endif
85 	TAILQ_HEAD(, ses_sensor) sc_sensors;
86 	struct ksensordev	sc_sensordev;
87 	struct sensor_task	*sc_sensortask;
88 };
89 
90 struct cfattach ses_ca = {
91 	sizeof(struct ses_softc), ses_match, ses_attach, ses_detach
92 };
93 
94 struct cfdriver ses_cd = {
95 	NULL, "ses", DV_DULL
96 };
97 
98 #define DEVNAME(s)	((s)->sc_dev.dv_xname)
99 
100 #define SES_BUFLEN	2048 /* XXX is this enough? */
101 
102 int	ses_read_config(struct ses_softc *);
103 int	ses_read_status(struct ses_softc *);
104 int	ses_make_sensors(struct ses_softc *, struct ses_type_desc *, int);
105 void	ses_refresh_sensors(void *);
106 
107 #if NBIO > 0
108 int	ses_ioctl(struct device *, u_long, caddr_t);
109 int	ses_write_config(struct ses_softc *);
110 int	ses_bio_blink(struct ses_softc *, struct bioc_blink *);
111 #endif
112 
113 void	ses_psu2sensor(struct ses_softc *, struct ses_sensor *);
114 void	ses_cool2sensor(struct ses_softc *, struct ses_sensor *);
115 void	ses_temp2sensor(struct ses_softc *, struct ses_sensor *);
116 
117 #ifdef SES_DEBUG
118 void	ses_dump_enc_desc(struct ses_enc_desc *);
119 char	*ses_dump_enc_string(u_char *, ssize_t);
120 #endif
121 
122 int
123 ses_match(struct device *parent, void *match, void *aux)
124 {
125 	struct scsi_attach_args		*sa = aux;
126 	struct scsi_inquiry_data	*inq = sa->sa_inqbuf;
127 
128 	if (inq == NULL)
129 		return (0);
130 
131 	if ((inq->device & SID_TYPE) == T_ENCLOSURE &&
132 	    SCSISPC(inq->version) >= 2)
133 		return (2);
134 
135 	/* match on dell enclosures */
136 	if ((inq->device & SID_TYPE) == T_PROCESSOR &&
137 	    SCSISPC(inq->version) == 3)
138 		return (3);
139 
140 	return (0);
141 }
142 
143 void
144 ses_attach(struct device *parent, struct device *self, void *aux)
145 {
146 	struct ses_softc		*sc = (struct ses_softc *)self;
147 	struct scsi_attach_args		*sa = aux;
148 	char				vendor[33];
149 	struct ses_sensor		*sensor;
150 #if NBIO > 0
151 	struct ses_slot			*slot;
152 #endif
153 
154 	sc->sc_link = sa->sa_sc_link;
155 	sa->sa_sc_link->device_softc = sc;
156 	rw_init(&sc->sc_lock, DEVNAME(sc));
157 
158 	scsi_strvis(vendor, sc->sc_link->inqdata.vendor,
159 	    sizeof(sc->sc_link->inqdata.vendor));
160 	if (strncasecmp(vendor, "Dell", sizeof(vendor)) == 0)
161 		sc->sc_enctype = SES_ENC_DELL;
162 	else
163 		sc->sc_enctype = SES_ENC_STD;
164 
165 	printf("\n");
166 
167 	if (ses_read_config(sc) != 0) {
168 		printf("%s: unable to read enclosure configuration\n",
169 		    DEVNAME(sc));
170 		return;
171 	}
172 
173 	if (!TAILQ_EMPTY(&sc->sc_sensors)) {
174 		sc->sc_sensortask = sensor_task_register(sc,
175 		    ses_refresh_sensors, 10);
176 		if (sc->sc_sensortask == NULL) {
177 			printf("%s: unable to register update task\n",
178 			    DEVNAME(sc));
179 			while (!TAILQ_EMPTY(&sc->sc_sensors)) {
180 				sensor = TAILQ_FIRST(&sc->sc_sensors);
181 				TAILQ_REMOVE(&sc->sc_sensors, sensor,
182 				    se_entry);
183 				free(sensor, M_DEVBUF);
184 			}
185 		} else {
186 			TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry)
187 				sensor_attach(&sc->sc_sensordev,
188 				    &sensor->se_sensor);
189 			sensordev_install(&sc->sc_sensordev);
190 		}
191 	}
192 
193 #if NBIO > 0
194 	if (!TAILQ_EMPTY(&sc->sc_slots) &&
195 	    bio_register(self, ses_ioctl) != 0) {
196 		printf("%s: unable to register ioctl\n", DEVNAME(sc));
197 		while (!TAILQ_EMPTY(&sc->sc_slots)) {
198 			slot = TAILQ_FIRST(&sc->sc_slots);
199 			TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
200 			free(slot, M_DEVBUF);
201 		}
202 	}
203 #endif
204 
205 	if (TAILQ_EMPTY(&sc->sc_sensors)
206 #if NBIO > 0
207 	    && TAILQ_EMPTY(&sc->sc_slots)
208 #endif
209 	    ) {
210 		free(sc->sc_buf, M_DEVBUF);
211 		sc->sc_buf = NULL;
212 	}
213 }
214 
215 int
216 ses_detach(struct device *self, int flags)
217 {
218 	struct ses_softc		*sc = (struct ses_softc *)self;
219 	struct ses_sensor		*sensor;
220 #if NBIO > 0
221 	struct ses_slot			*slot;
222 #endif
223 
224 	rw_enter_write(&sc->sc_lock);
225 
226 #if NBIO > 0
227 	if (!TAILQ_EMPTY(&sc->sc_slots)) {
228 		bio_unregister(self);
229 		while (!TAILQ_EMPTY(&sc->sc_slots)) {
230 			slot = TAILQ_FIRST(&sc->sc_slots);
231 			TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
232 			free(slot, M_DEVBUF);
233 		}
234 	}
235 #endif
236 
237 	if (!TAILQ_EMPTY(&sc->sc_sensors)) {
238 		sensordev_deinstall(&sc->sc_sensordev);
239 		sensor_task_unregister(sc->sc_sensortask);
240 
241 		while (!TAILQ_EMPTY(&sc->sc_sensors)) {
242 			sensor = TAILQ_FIRST(&sc->sc_sensors);
243 			sensor_detach(&sc->sc_sensordev, &sensor->se_sensor);
244 			TAILQ_REMOVE(&sc->sc_sensors, sensor, se_entry);
245 			free(sensor, M_DEVBUF);
246 		}
247 	}
248 
249 	if (sc->sc_buf != NULL)
250 		free(sc->sc_buf, M_DEVBUF);
251 
252 	rw_exit_write(&sc->sc_lock);
253 
254 	return (0);
255 }
256 
257 int
258 ses_read_config(struct ses_softc *sc)
259 {
260 	struct ses_scsi_diag		cmd;
261 	int				flags;
262 
263 	u_char				*buf, *p;
264 
265 	struct ses_config_hdr		*cfg;
266 	struct ses_enc_hdr		*enc;
267 #ifdef SES_DEBUG
268 	struct ses_enc_desc		*desc;
269 #endif
270 	struct ses_type_desc		*tdh, *tdlist;
271 
272 	int				i, ntypes = 0, nelems = 0;
273 
274 	buf = malloc(SES_BUFLEN, M_DEVBUF, M_NOWAIT | M_ZERO);
275 	if (buf == NULL)
276 		return (1);
277 
278 	memset(&cmd, 0, sizeof(cmd));
279 	cmd.opcode = RECEIVE_DIAGNOSTIC;
280 	cmd.flags |= SES_DIAG_PCV;
281 	cmd.pgcode = SES_PAGE_CONFIG;
282 	cmd.length = htobe16(SES_BUFLEN);
283 	flags = SCSI_DATA_IN;
284 #ifndef SCSIDEBUG
285 	flags |= SCSI_SILENT;
286 #endif
287 
288 	if (cold)
289 		flags |= SCSI_AUTOCONF;
290 
291 	if (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
292 	    sizeof(cmd), buf, SES_BUFLEN, 2, 3000, NULL, flags) != 0) {
293 		free(buf, M_DEVBUF);
294 		return (1);
295 	}
296 
297 	cfg = (struct ses_config_hdr *)buf;
298 	if (cfg->pgcode != cmd.pgcode || betoh16(cfg->length) > SES_BUFLEN) {
299 		free(buf, M_DEVBUF);
300 		return (1);
301 	}
302 
303 	DPRINTF("%s: config: n_subenc: %d length: %d\n", DEVNAME(sc),
304 	    cfg->n_subenc, betoh16(cfg->length));
305 
306 	p = buf + SES_CFG_HDRLEN;
307 	for (i = 0; i <= cfg->n_subenc; i++) {
308 		enc = (struct ses_enc_hdr *)p;
309 #ifdef SES_DEBUG
310 		DPRINTF("%s: enclosure %d enc_id: 0x%02x n_types: %d\n",
311 		    DEVNAME(sc), i, enc->enc_id, enc->n_types);
312 		desc = (struct ses_enc_desc *)(p + SES_ENC_HDRLEN);
313 		ses_dump_enc_desc(desc);
314 #endif /* SES_DEBUG */
315 
316 		ntypes += enc->n_types;
317 
318 		p += SES_ENC_HDRLEN + enc->vendor_len;
319 	}
320 
321 	tdlist = (struct ses_type_desc *)p; /* stash this for later */
322 
323 	for (i = 0; i < ntypes; i++) {
324 		tdh = (struct ses_type_desc *)p;
325 		DPRINTF("%s: td %d subenc_id: %d type 0x%02x n_elem: %d\n",
326 		    DEVNAME(sc), i, tdh->subenc_id, tdh->type, tdh->n_elem);
327 
328 		nelems += tdh->n_elem;
329 
330 		p += SES_TYPE_DESCLEN;
331 	}
332 
333 #ifdef SES_DEBUG
334 	for (i = 0; i < ntypes; i++) {
335 		DPRINTF("%s: td %d '%s'\n", DEVNAME(sc), i,
336 		    ses_dump_enc_string(p, tdlist[i].desc_len));
337 
338 		p += tdlist[i].desc_len;
339 	}
340 #endif /* SES_DEBUG */
341 
342 	sc->sc_buflen = SES_STAT_LEN(ntypes, nelems);
343 	sc->sc_buf = malloc(sc->sc_buflen, M_DEVBUF, M_NOWAIT);
344 	if (sc->sc_buf == NULL) {
345 		free(buf, M_DEVBUF);
346 		return (1);
347 	}
348 
349 	/* get the status page and then use it to generate a list of sensors */
350 	if (ses_make_sensors(sc, tdlist, ntypes) != 0) {
351 		free(buf, M_DEVBUF);
352 		free(sc->sc_buf, M_DEVBUF);
353 		return (1);
354 	}
355 
356 	free(buf, M_DEVBUF);
357 	return (0);
358 }
359 
360 int
361 ses_read_status(struct ses_softc *sc)
362 {
363 	struct ses_scsi_diag		cmd;
364 	int				flags;
365 
366 	memset(&cmd, 0, sizeof(cmd));
367 	cmd.opcode = RECEIVE_DIAGNOSTIC;
368 	cmd.flags |= SES_DIAG_PCV;
369 	cmd.pgcode = SES_PAGE_STATUS;
370 	cmd.length = htobe16(sc->sc_buflen);
371 	flags = SCSI_DATA_IN;
372 #ifndef SCSIDEBUG
373 	flags |= SCSI_SILENT;
374 #endif
375 	if (cold)
376 		flags |= SCSI_AUTOCONF;
377 
378 	if (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
379 	    sizeof(cmd), sc->sc_buf, sc->sc_buflen, 2, 3000, NULL, flags) != 0)
380 		return (1);
381 
382 	return (0);
383 }
384 
385 int
386 ses_make_sensors(struct ses_softc *sc, struct ses_type_desc *types, int ntypes)
387 {
388 	struct ses_status		*status;
389 	struct ses_sensor		*sensor;
390 #if NBIO > 0
391 	struct ses_slot			*slot;
392 #endif
393 	enum sensor_type		stype;
394 	char				*fmt;
395 	int				i, j;
396 
397 	if (ses_read_status(sc) != 0)
398 		return (1);
399 
400 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
401 	    sizeof(sc->sc_sensordev.xname));
402 
403 	TAILQ_INIT(&sc->sc_sensors);
404 #if NBIO > 0
405 	TAILQ_INIT(&sc->sc_slots);
406 #endif
407 
408 	status = (struct ses_status *)(sc->sc_buf + SES_STAT_HDRLEN);
409 	for (i = 0; i < ntypes; i++) {
410 		/* ignore the overall status element for this type */
411 		DPRINTFN(1, "%s: %3d:-   0x%02x 0x%02x%02x%02x type: 0x%02x\n",
412 		     DEVNAME(sc), i, status->com, status->f1, status->f2,
413 		    status->f3, types[i].type);
414 
415 		for (j = 0; j < types[i].n_elem; j++) {
416 			/* move to the current status element */
417 			status++;
418 
419 			DPRINTFN(1, "%s: %3d:%-3d 0x%02x 0x%02x%02x%02x\n",
420 			    DEVNAME(sc), i, j, status->com, status->f1,
421 			    status->f2, status->f3);
422 
423 			if (SES_STAT_CODE(status->com) == SES_STAT_CODE_NOTINST)
424 				continue;
425 
426 			switch (types[i].type) {
427 #if NBIO > 0
428 			case SES_T_DEVICE:
429 				slot = malloc(sizeof(*slot), M_DEVBUF,
430 				    M_NOWAIT | M_ZERO);
431 				if (slot == NULL)
432 					goto error;
433 
434 				slot->sl_stat = status;
435 
436 				TAILQ_INSERT_TAIL(&sc->sc_slots, slot,
437 				    sl_entry);
438 
439 				continue;
440 #endif
441 
442 			case SES_T_POWERSUPPLY:
443 				stype = SENSOR_INDICATOR;
444 				fmt = "PSU";
445 				break;
446 
447 			case SES_T_COOLING:
448 				stype = SENSOR_PERCENT;
449 				fmt = "Fan";
450 				break;
451 
452 			case SES_T_TEMP:
453 				stype = SENSOR_TEMP;
454 				fmt = "";
455 				break;
456 
457 			default:
458 				continue;
459 			}
460 
461 			sensor = malloc(sizeof(*sensor), M_DEVBUF,
462 			    M_NOWAIT | M_ZERO);
463 			if (sensor == NULL)
464 				goto error;
465 
466 			sensor->se_type = types[i].type;
467 			sensor->se_stat = status;
468 			sensor->se_sensor.type = stype;
469 			strlcpy(sensor->se_sensor.desc, fmt,
470 			    sizeof(sensor->se_sensor.desc));
471 
472 			TAILQ_INSERT_TAIL(&sc->sc_sensors, sensor, se_entry);
473 		}
474 
475 		/* move to the overall status element of the next type */
476 		status++;
477 	}
478 
479 	return (0);
480 error:
481 #if NBIO > 0
482 	while (!TAILQ_EMPTY(&sc->sc_slots)) {
483 		slot = TAILQ_FIRST(&sc->sc_slots);
484 		TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
485 		free(slot, M_DEVBUF);
486 	}
487 #endif
488 	while (!TAILQ_EMPTY(&sc->sc_sensors)) {
489 		sensor = TAILQ_FIRST(&sc->sc_sensors);
490 		TAILQ_REMOVE(&sc->sc_sensors, sensor, se_entry);
491 		free(sensor, M_DEVBUF);
492 	}
493 	return (1);
494 }
495 
496 void
497 ses_refresh_sensors(void *arg)
498 {
499 	struct ses_softc		*sc = (struct ses_softc *)arg;
500 	struct ses_sensor		*sensor;
501 	int				ret = 0;
502 
503 	rw_enter_write(&sc->sc_lock);
504 
505 	if (ses_read_status(sc) != 0) {
506 		rw_exit_write(&sc->sc_lock);
507 		return;
508 	}
509 
510 	TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry) {
511 		DPRINTFN(10, "%s: %s 0x%02x 0x%02x%02x%02x\n", DEVNAME(sc),
512 		    sensor->se_sensor.desc, sensor->se_stat->com,
513 		    sensor->se_stat->f1, sensor->se_stat->f2,
514 		    sensor->se_stat->f3);
515 
516 		switch (SES_STAT_CODE(sensor->se_stat->com)) {
517 		case SES_STAT_CODE_OK:
518 			sensor->se_sensor.status = SENSOR_S_OK;
519 			break;
520 
521 		case SES_STAT_CODE_CRIT:
522 		case SES_STAT_CODE_UNREC:
523 			sensor->se_sensor.status = SENSOR_S_CRIT;
524 			break;
525 
526 		case SES_STAT_CODE_NONCRIT:
527 			sensor->se_sensor.status = SENSOR_S_WARN;
528 			break;
529 
530 		case SES_STAT_CODE_NOTINST:
531 		case SES_STAT_CODE_UNKNOWN:
532 		case SES_STAT_CODE_NOTAVAIL:
533 			sensor->se_sensor.status = SENSOR_S_UNKNOWN;
534 			break;
535 		}
536 
537 		switch (sensor->se_type) {
538 		case SES_T_POWERSUPPLY:
539 			ses_psu2sensor(sc, sensor);
540 			break;
541 
542 		case SES_T_COOLING:
543 			ses_cool2sensor(sc, sensor);
544 			break;
545 
546 		case SES_T_TEMP:
547 			ses_temp2sensor(sc, sensor);
548 			break;
549 
550 		default:
551 			ret = 1;
552 			break;
553 		}
554 	}
555 
556 	rw_exit_write(&sc->sc_lock);
557 
558 	if (ret)
559 		printf("%s: error in sensor data\n", DEVNAME(sc));
560 }
561 
562 #if NBIO > 0
563 int
564 ses_ioctl(struct device *dev, u_long cmd, caddr_t addr)
565 {
566 	struct ses_softc		*sc = (struct ses_softc *)dev;
567 	int				error = 0;
568 
569 	switch (cmd) {
570 	case BIOCBLINK:
571 		error = ses_bio_blink(sc, (struct bioc_blink *)addr);
572 		break;
573 
574 	default:
575 		error = EINVAL;
576 		break;
577 	}
578 
579 	return (error);
580 }
581 
582 int
583 ses_write_config(struct ses_softc *sc)
584 {
585 	struct ses_scsi_diag		cmd;
586 	int				flags;
587 
588 	memset(&cmd, 0, sizeof(cmd));
589 	cmd.opcode = SEND_DIAGNOSTIC;
590 	cmd.flags |= SES_DIAG_PF;
591 	cmd.length = htobe16(sc->sc_buflen);
592 	flags = SCSI_DATA_OUT;
593 #ifndef SCSIDEBUG
594 	flags |= SCSI_SILENT;
595 #endif
596 
597 	if (cold)
598 		flags |= SCSI_AUTOCONF;
599 
600 	if (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd,
601 	    sizeof(cmd), sc->sc_buf, sc->sc_buflen, 2, 3000, NULL, flags) != 0)
602 		return (1);
603 
604 	return (0);
605 }
606 
607 int
608 ses_bio_blink(struct ses_softc *sc, struct bioc_blink *blink)
609 {
610 	struct ses_slot			*slot;
611 
612 	rw_enter_write(&sc->sc_lock);
613 
614 	if (ses_read_status(sc) != 0) {
615 		rw_exit_write(&sc->sc_lock);
616 		return (EIO);
617 	}
618 
619 	TAILQ_FOREACH(slot, &sc->sc_slots, sl_entry) {
620 		if (slot->sl_stat->f1 == blink->bb_target)
621 			break;
622 	}
623 
624 	if (slot == TAILQ_END(&sc->sc_slots)) {
625 		rw_exit_write(&sc->sc_lock);
626 		return (EINVAL);
627 	}
628 
629 	DPRINTFN(3, "%s: 0x%02x 0x%02x 0x%02x 0x%02x\n", DEVNAME(sc),
630 	    slot->sl_stat->com, slot->sl_stat->f1, slot->sl_stat->f2,
631 	    slot->sl_stat->f3);
632 
633 	slot->sl_stat->com = SES_STAT_SELECT;
634 	slot->sl_stat->f2 &= SES_C_DEV_F2MASK;
635 	slot->sl_stat->f3 &= SES_C_DEV_F3MASK;
636 
637 	switch (blink->bb_status) {
638 	case BIOC_SBUNBLINK:
639 		slot->sl_stat->f2 &= ~SES_C_DEV_IDENT;
640 		break;
641 
642 	case BIOC_SBBLINK:
643 		slot->sl_stat->f2 |= SES_C_DEV_IDENT;
644 		break;
645 
646 	default:
647 		rw_exit_write(&sc->sc_lock);
648 		return (EINVAL);
649 	}
650 
651 	DPRINTFN(3, "%s: 0x%02x 0x%02x 0x%02x 0x%02x\n", DEVNAME(sc),
652 	    slot->sl_stat->com, slot->sl_stat->f1, slot->sl_stat->f2,
653 	    slot->sl_stat->f3);
654 
655 	if (ses_write_config(sc) != 0) {
656 		rw_exit_write(&sc->sc_lock);
657 		return (EIO);
658 	}
659 
660 	rw_exit_write(&sc->sc_lock);
661 
662 	return (0);
663 }
664 #endif
665 
666 void
667 ses_psu2sensor(struct ses_softc *sc, struct ses_sensor *s)
668 {
669 	s->se_sensor.value = SES_S_PSU_OFF(s->se_stat) ? 0 : 1;
670 }
671 
672 void
673 ses_cool2sensor(struct ses_softc *sc, struct ses_sensor *s)
674 {
675 	switch (sc->sc_enctype) {
676 	case SES_ENC_STD:
677 		switch (SES_S_COOL_CODE(s->se_stat)) {
678 		case SES_S_COOL_C_STOPPED:
679 			s->se_sensor.value = 0;
680 			break;
681 		case SES_S_COOL_C_LOW1:
682 		case SES_S_COOL_C_LOW2:
683 		case SES_S_COOL_C_LOW3:
684 			s->se_sensor.value = 33333;
685 			break;
686 		case SES_S_COOL_C_INTER:
687 		case SES_S_COOL_C_HI3:
688 		case SES_S_COOL_C_HI2:
689 			s->se_sensor.value = 66666;
690 			break;
691 		case SES_S_COOL_C_HI1:
692 			s->se_sensor.value = 100000;
693 			break;
694 		}
695 		break;
696 
697 	/* Dell only use the first three codes to represent speed */
698 	case SES_ENC_DELL:
699 		switch (SES_S_COOL_CODE(s->se_stat)) {
700 		case SES_S_COOL_C_STOPPED:
701 			s->se_sensor.value = 0;
702 			break;
703 		case SES_S_COOL_C_LOW1:
704 			s->se_sensor.value = 33333;
705 			break;
706 		case SES_S_COOL_C_LOW2:
707 			s->se_sensor.value = 66666;
708 			break;
709 		case SES_S_COOL_C_LOW3:
710 		case SES_S_COOL_C_INTER:
711 		case SES_S_COOL_C_HI3:
712 		case SES_S_COOL_C_HI2:
713 		case SES_S_COOL_C_HI1:
714 			s->se_sensor.value = 100000;
715 			break;
716 		}
717 		break;
718 	}
719 }
720 
721 void
722 ses_temp2sensor(struct ses_softc *sc, struct ses_sensor *s)
723 {
724 	s->se_sensor.value = (int64_t)SES_S_TEMP(s->se_stat);
725 	s->se_sensor.value += SES_S_TEMP_OFFSET;
726 	s->se_sensor.value *= 1000000; /* convert to micro (mu) degrees */
727 	s->se_sensor.value += 273150000; /* convert to kelvin */
728 }
729 
730 #ifdef SES_DEBUG
731 void
732 ses_dump_enc_desc(struct ses_enc_desc *desc)
733 {
734 	char				str[32];
735 
736 #if 0
737 	/* XXX not a string. wwn? */
738 	memset(str, 0, sizeof(str));
739 	memcpy(str, desc->logical_id, sizeof(desc->logical_id));
740 	DPRINTF("logical_id: %s", str);
741 #endif
742 
743 	memset(str, 0, sizeof(str));
744 	memcpy(str, desc->vendor_id, sizeof(desc->vendor_id));
745 	DPRINTF(" vendor_id: %s", str);
746 
747 	memset(str, 0, sizeof(str));
748 	memcpy(str, desc->prod_id, sizeof(desc->prod_id));
749 	DPRINTF(" prod_id: %s", str);
750 
751 	memset(str, 0, sizeof(str));
752 	memcpy(str, desc->prod_rev, sizeof(desc->prod_rev));
753 	DPRINTF(" prod_rev: %s\n", str);
754 }
755 
756 char *
757 ses_dump_enc_string(u_char *buf, ssize_t len)
758 {
759 	static char			str[256];
760 
761 	memset(str, 0, sizeof(str));
762 	if (len > 0)
763 		memcpy(str, buf, len);
764 
765 	return (str);
766 }
767 #endif /* SES_DEBUG */
768