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