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