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)
43int	sesdebug = 2;
44#else
45#define DPRINTF(x...)		/* x */
46#define DPRINTFN(n,x...)	/* n: x */
47#endif /* SES_DEBUG */
48
49int	ses_match(struct device *, void *, void *);
50void	ses_attach(struct device *, struct device *, void *);
51int	ses_detach(struct device *, int);
52
53struct 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
62struct ses_slot {
63	struct ses_status	*sl_stat;
64
65	TAILQ_ENTRY(ses_slot)	 sl_entry;
66};
67#endif /* NBIO > 0 */
68
69struct 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
90const struct cfattach ses_ca = {
91	sizeof(struct ses_softc), ses_match, ses_attach, ses_detach
92};
93
94struct 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
102int	ses_read_config(struct ses_softc *);
103int	ses_read_status(struct ses_softc *);
104int	ses_make_sensors(struct ses_softc *, struct ses_type_desc *, int);
105void	ses_refresh_sensors(void *);
106
107#if NBIO > 0
108int	ses_ioctl(struct device *, u_long, caddr_t);
109int	ses_write_config(struct ses_softc *);
110int	ses_bio_blink(struct ses_softc *, struct bioc_blink *);
111#endif /* NBIO > 0 */
112
113void	ses_psu2sensor(struct ses_softc *, struct ses_sensor *);
114void	ses_cool2sensor(struct ses_softc *, struct ses_sensor *);
115void	ses_temp2sensor(struct ses_softc *, struct ses_sensor *);
116
117#ifdef SES_DEBUG
118void	 ses_dump_enc_desc(struct ses_enc_desc *);
119char	*ses_dump_enc_string(u_char *, ssize_t);
120#endif /* SES_DEBUG */
121
122int
123ses_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
140void
141ses_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
212int
213ses_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
254int
255ses_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
360done:
361	if (buf)
362		dma_free(buf, SES_BUFLEN);
363	return error;
364}
365
366int
367ses_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
399int
400ses_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;
494error:
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
510void
511ses_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
577int
578ses_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
596int
597ses_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
629int
630ses_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
688void
689ses_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
694void
695ses_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
743void
744ses_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
753void
754ses_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
778char *
779ses_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