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