xref: /openbsd-src/sys/scsi/ses.c (revision 9eaf72d1e5ccbb9efcde4ef7f9cb4287f8996489)
1*9eaf72d1Smpi /*	$OpenBSD: ses.c,v 1.64 2021/10/24 16:57:30 mpi Exp $ */
2d506901aSdlg 
3d506901aSdlg /*
4d506901aSdlg  * Copyright (c) 2005 David Gwynne <dlg@openbsd.org>
5d506901aSdlg  *
6d506901aSdlg  * Permission to use, copy, modify, and distribute this software for any
7d506901aSdlg  * purpose with or without fee is hereby granted, provided that the above
8d506901aSdlg  * copyright notice and this permission notice appear in all copies.
9d506901aSdlg  *
10d506901aSdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d506901aSdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d506901aSdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d506901aSdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d506901aSdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d506901aSdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d506901aSdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d506901aSdlg  */
18d506901aSdlg 
196e1ed140Sdlg #include "bio.h"
206e1ed140Sdlg 
21d506901aSdlg #include <sys/param.h>
22d506901aSdlg #include <sys/systm.h>
2316667ae2Sdlg #include <sys/device.h>
24d506901aSdlg #include <sys/scsiio.h>
25d506901aSdlg #include <sys/malloc.h>
265c0b007dSderaadt #include <sys/pool.h>
2734e1a95bSkrw #include <sys/rwlock.h>
28d506901aSdlg #include <sys/queue.h>
29d506901aSdlg #include <sys/sensors.h>
30d506901aSdlg 
316e1ed140Sdlg #if NBIO > 0
326e1ed140Sdlg #include <dev/biovar.h>
33364ebb70Skrw #endif /* NBIO > 0 */
346e1ed140Sdlg 
35d506901aSdlg #include <scsi/scsi_all.h>
36d506901aSdlg #include <scsi/scsiconf.h>
37d506901aSdlg 
38d506901aSdlg #include <scsi/ses.h>
39d506901aSdlg 
40d506901aSdlg #ifdef SES_DEBUG
41d506901aSdlg #define DPRINTF(x...)		do { if (sesdebug) printf(x); } while (0)
42d506901aSdlg #define DPRINTFN(n, x...)	do { if (sesdebug > (n)) printf(x); } while (0)
43d506901aSdlg int	sesdebug = 2;
44d506901aSdlg #else
45d506901aSdlg #define DPRINTF(x...)		/* x */
46d506901aSdlg #define DPRINTFN(n,x...)	/* n: x */
47364ebb70Skrw #endif /* SES_DEBUG */
48d506901aSdlg 
49d506901aSdlg int	ses_match(struct device *, void *, void *);
50d506901aSdlg void	ses_attach(struct device *, struct device *, void *);
51d506901aSdlg int	ses_detach(struct device *, int);
52d506901aSdlg 
53d506901aSdlg struct ses_sensor {
54275cbf62Sderaadt 	struct ksensor		 se_sensor;
55d506901aSdlg 	u_int8_t		 se_type;
56d506901aSdlg 	struct ses_status	*se_stat;
57d506901aSdlg 
58d506901aSdlg 	TAILQ_ENTRY(ses_sensor)	se_entry;
59d506901aSdlg };
60d506901aSdlg 
616e1ed140Sdlg #if NBIO > 0
626e1ed140Sdlg struct ses_slot {
636e1ed140Sdlg 	struct ses_status	*sl_stat;
646e1ed140Sdlg 
656e1ed140Sdlg 	TAILQ_ENTRY(ses_slot)	 sl_entry;
666e1ed140Sdlg };
67364ebb70Skrw #endif /* NBIO > 0 */
686e1ed140Sdlg 
69d506901aSdlg struct ses_softc {
70d506901aSdlg 	struct device		 sc_dev;
71d506901aSdlg 	struct scsi_link	*sc_link;
7234e1a95bSkrw 	struct rwlock		 sc_lock;
73d506901aSdlg 
74d99c3651Sdlg 	enum {
75d99c3651Sdlg 		SES_ENC_STD,
76d99c3651Sdlg 		SES_ENC_DELL
77d99c3651Sdlg 	}			 sc_enctype;
78d99c3651Sdlg 
79d506901aSdlg 	u_char			*sc_buf;
80d506901aSdlg 	ssize_t			 sc_buflen;
81d506901aSdlg 
826e1ed140Sdlg #if NBIO > 0
836e1ed140Sdlg 	TAILQ_HEAD(, ses_slot)	 sc_slots;
84364ebb70Skrw #endif /* NBIO > 0 */
85d506901aSdlg 	TAILQ_HEAD(, ses_sensor) sc_sensors;
86275cbf62Sderaadt 	struct ksensordev	 sc_sensordev;
87abd9fc28Sdlg 	struct sensor_task	*sc_sensortask;
88d506901aSdlg };
89d506901aSdlg 
90*9eaf72d1Smpi const struct cfattach ses_ca = {
91d506901aSdlg 	sizeof(struct ses_softc), ses_match, ses_attach, ses_detach
92d506901aSdlg };
93d506901aSdlg 
94d506901aSdlg struct cfdriver ses_cd = {
95d506901aSdlg 	NULL, "ses", DV_DULL
96d506901aSdlg };
97d506901aSdlg 
98d506901aSdlg #define DEVNAME(s)	((s)->sc_dev.dv_xname)
99d506901aSdlg 
10043b5650cSkrw #define SES_BUFLEN	2048 /* XXX Is this enough? */
101d506901aSdlg 
102d506901aSdlg int	ses_read_config(struct ses_softc *);
1037351cb62Smarco int	ses_read_status(struct ses_softc *);
104d506901aSdlg int	ses_make_sensors(struct ses_softc *, struct ses_type_desc *, int);
1058503befaSdlg void	ses_refresh_sensors(void *);
106d506901aSdlg 
1076e1ed140Sdlg #if NBIO > 0
1086e1ed140Sdlg int	ses_ioctl(struct device *, u_long, caddr_t);
1096e1ed140Sdlg int	ses_write_config(struct ses_softc *);
1106e1ed140Sdlg int	ses_bio_blink(struct ses_softc *, struct bioc_blink *);
111364ebb70Skrw #endif /* NBIO > 0 */
1126e1ed140Sdlg 
113d99c3651Sdlg void	ses_psu2sensor(struct ses_softc *, struct ses_sensor *);
114d99c3651Sdlg void	ses_cool2sensor(struct ses_softc *, struct ses_sensor *);
115d99c3651Sdlg void	ses_temp2sensor(struct ses_softc *, struct ses_sensor *);
116d506901aSdlg 
117d506901aSdlg #ifdef SES_DEBUG
118d506901aSdlg void	 ses_dump_enc_desc(struct ses_enc_desc *);
119d506901aSdlg char	*ses_dump_enc_string(u_char *, ssize_t);
120364ebb70Skrw #endif /* SES_DEBUG */
121d506901aSdlg 
122d506901aSdlg int
ses_match(struct device * parent,void * match,void * aux)123d506901aSdlg ses_match(struct device *parent, void *match, void *aux)
124d506901aSdlg {
125a0837789Sdlg 	struct scsi_attach_args		*sa = aux;
1260fbd355cSkrw 	struct scsi_inquiry_data	*inq = &sa->sa_sc_link->inqdata;
127d506901aSdlg 
1287b732f28Skrw 	if ((inq->device & SID_TYPE) == T_ENCLOSURE &&
1297b732f28Skrw 	    SID_ANSII_REV(inq) >= SCSI_REV_2)
13043b5650cSkrw 		return 2;
131d506901aSdlg 
13243b5650cSkrw 	/* Match on Dell enclosures. */
1335382ead8Smarco 	if ((inq->device & SID_TYPE) == T_PROCESSOR &&
1347b732f28Skrw 	    SID_ANSII_REV(inq) == SCSI_REV_SPC)
13543b5650cSkrw 		return 3;
1365382ead8Smarco 
13743b5650cSkrw 	return 0;
138d506901aSdlg }
139d506901aSdlg 
140d506901aSdlg void
ses_attach(struct device * parent,struct device * self,void * aux)141d506901aSdlg ses_attach(struct device *parent, struct device *self, void *aux)
142d506901aSdlg {
14343b5650cSkrw 	char				 vendor[33];
144d506901aSdlg 	struct ses_softc		*sc = (struct ses_softc *)self;
145a0837789Sdlg 	struct scsi_attach_args		*sa = aux;
1468503befaSdlg 	struct ses_sensor		*sensor;
1478503befaSdlg #if NBIO > 0
1488503befaSdlg 	struct ses_slot			*slot;
149364ebb70Skrw #endif /* NBIO > 0 */
150d506901aSdlg 
151d506901aSdlg 	sc->sc_link = sa->sa_sc_link;
152ba35ccc6Smarco 	sa->sa_sc_link->device_softc = sc;
15334e1a95bSkrw 	rw_init(&sc->sc_lock, DEVNAME(sc));
154d506901aSdlg 
1551baa716cSdlg 	scsi_strvis(vendor, sc->sc_link->inqdata.vendor,
1561baa716cSdlg 	    sizeof(sc->sc_link->inqdata.vendor));
1571baa716cSdlg 	if (strncasecmp(vendor, "Dell", sizeof(vendor)) == 0)
158d99c3651Sdlg 		sc->sc_enctype = SES_ENC_DELL;
159d99c3651Sdlg 	else
160d99c3651Sdlg 		sc->sc_enctype = SES_ENC_STD;
161d99c3651Sdlg 
162d506901aSdlg 	printf("\n");
163d506901aSdlg 
164c28a34ecSdlg 	if (ses_read_config(sc) != 0) {
165c28a34ecSdlg 		printf("%s: unable to read enclosure configuration\n",
166c28a34ecSdlg 		    DEVNAME(sc));
167c28a34ecSdlg 		return;
168c28a34ecSdlg 	}
169ceb95311Sdlg 
170abd9fc28Sdlg 	if (!TAILQ_EMPTY(&sc->sc_sensors)) {
171abd9fc28Sdlg 		sc->sc_sensortask = sensor_task_register(sc,
172abd9fc28Sdlg 		    ses_refresh_sensors, 10);
173abd9fc28Sdlg 		if (sc->sc_sensortask == NULL) {
174abd9fc28Sdlg 			printf("%s: unable to register update task\n",
175abd9fc28Sdlg 			    DEVNAME(sc));
1768503befaSdlg 			while (!TAILQ_EMPTY(&sc->sc_sensors)) {
1778503befaSdlg 				sensor = TAILQ_FIRST(&sc->sc_sensors);
178abd9fc28Sdlg 				TAILQ_REMOVE(&sc->sc_sensors, sensor,
179abd9fc28Sdlg 				    se_entry);
180a67129dbStedu 				free(sensor, M_DEVBUF, sizeof(*sensor));
1818503befaSdlg 			}
1828503befaSdlg 		} else {
1838503befaSdlg 			TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry)
184abd9fc28Sdlg 				sensor_attach(&sc->sc_sensordev,
185abd9fc28Sdlg 				    &sensor->se_sensor);
18627515a6bSderaadt 			sensordev_install(&sc->sc_sensordev);
1878503befaSdlg 		}
188abd9fc28Sdlg 	}
1898503befaSdlg 
1908503befaSdlg #if NBIO > 0
1918503befaSdlg 	if (!TAILQ_EMPTY(&sc->sc_slots) &&
1928503befaSdlg 	    bio_register(self, ses_ioctl) != 0) {
1938503befaSdlg 		printf("%s: unable to register ioctl\n", DEVNAME(sc));
1948503befaSdlg 		while (!TAILQ_EMPTY(&sc->sc_slots)) {
1958503befaSdlg 			slot = TAILQ_FIRST(&sc->sc_slots);
1968503befaSdlg 			TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
197a67129dbStedu 			free(slot, M_DEVBUF, sizeof(*slot));
1988503befaSdlg 		}
1998503befaSdlg 	}
200364ebb70Skrw #endif /* NBIO > 0 */
2018503befaSdlg 
202b6c5cf15Sdlg 	if (TAILQ_EMPTY(&sc->sc_sensors)
203b6c5cf15Sdlg #if NBIO > 0
204b6c5cf15Sdlg 	    && TAILQ_EMPTY(&sc->sc_slots)
205364ebb70Skrw #endif /* NBIO > 0 */
206b6c5cf15Sdlg 	    ) {
2075c0b007dSderaadt 		dma_free(sc->sc_buf, sc->sc_buflen);
208b6c5cf15Sdlg 		sc->sc_buf = NULL;
209b6c5cf15Sdlg 	}
210d506901aSdlg }
211d506901aSdlg 
212d506901aSdlg int
ses_detach(struct device * self,int flags)213d506901aSdlg ses_detach(struct device *self, int flags)
214d506901aSdlg {
215d506901aSdlg 	struct ses_softc		*sc = (struct ses_softc *)self;
216d506901aSdlg 	struct ses_sensor		*sensor;
2176e1ed140Sdlg #if NBIO > 0
2186e1ed140Sdlg 	struct ses_slot			*slot;
219364ebb70Skrw #endif /* NBIO > 0 */
220d506901aSdlg 
22134e1a95bSkrw 	rw_enter_write(&sc->sc_lock);
222cc62d9f9Sdlg 
2236e1ed140Sdlg #if NBIO > 0
2248503befaSdlg 	if (!TAILQ_EMPTY(&sc->sc_slots)) {
2256e1ed140Sdlg 		bio_unregister(self);
2266e1ed140Sdlg 		while (!TAILQ_EMPTY(&sc->sc_slots)) {
2276e1ed140Sdlg 			slot = TAILQ_FIRST(&sc->sc_slots);
2286e1ed140Sdlg 			TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
229a67129dbStedu 			free(slot, M_DEVBUF, sizeof(*slot));
2306e1ed140Sdlg 		}
2318503befaSdlg 	}
232364ebb70Skrw #endif /* NBIO > 0 */
2336e1ed140Sdlg 
2348503befaSdlg 	if (!TAILQ_EMPTY(&sc->sc_sensors)) {
23527515a6bSderaadt 		sensordev_deinstall(&sc->sc_sensordev);
236abd9fc28Sdlg 		sensor_task_unregister(sc->sc_sensortask);
237d506901aSdlg 
2383db94aacSgrange 		while (!TAILQ_EMPTY(&sc->sc_sensors)) {
2393db94aacSgrange 			sensor = TAILQ_FIRST(&sc->sc_sensors);
24027515a6bSderaadt 			sensor_detach(&sc->sc_sensordev, &sensor->se_sensor);
2413db94aacSgrange 			TAILQ_REMOVE(&sc->sc_sensors, sensor, se_entry);
242a67129dbStedu 			free(sensor, M_DEVBUF, sizeof(*sensor));
2433db94aacSgrange 		}
244d506901aSdlg 	}
245d506901aSdlg 
2468503befaSdlg 	if (sc->sc_buf != NULL)
2475c0b007dSderaadt 		dma_free(sc->sc_buf, sc->sc_buflen);
2488503befaSdlg 
24934e1a95bSkrw 	rw_exit_write(&sc->sc_lock);
250cc62d9f9Sdlg 
25143b5650cSkrw 	return 0;
252d506901aSdlg }
253d506901aSdlg 
254d506901aSdlg int
ses_read_config(struct ses_softc * sc)255d506901aSdlg ses_read_config(struct ses_softc *sc)
256d506901aSdlg {
257f8bbb75aSkrw 	struct ses_scsi_diag		*cmd;
258d506901aSdlg 	struct ses_config_hdr		*cfg;
259f8bbb75aSkrw 	struct ses_type_desc		*tdh, *tdlist;
260d506901aSdlg #ifdef SES_DEBUG
261d506901aSdlg 	struct ses_enc_desc		*desc;
262364ebb70Skrw #endif /* SES_DEBUG */
263f8bbb75aSkrw 	struct ses_enc_hdr		*enc;
264f8bbb75aSkrw 	struct scsi_xfer		*xs;
265f8bbb75aSkrw 	u_char				*buf, *p;
2665c0b007dSderaadt 	int				 error = 0, i;
267f8bbb75aSkrw 	int				 flags = 0, ntypes = 0, nelems = 0;
268d506901aSdlg 
2695c0b007dSderaadt 	buf = dma_alloc(SES_BUFLEN, PR_NOWAIT | PR_ZERO);
270d506901aSdlg 	if (buf == NULL)
27143b5650cSkrw 		return 1;
272d506901aSdlg 
2737351cb62Smarco 	if (cold)
274dacf4336Skrw 		SET(flags, SCSI_AUTOCONF);
275f8bbb75aSkrw 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
276f8bbb75aSkrw 	if (xs == NULL) {
2775c0b007dSderaadt 		error = 1;
2785c0b007dSderaadt 		goto done;
279f8bbb75aSkrw 	}
280f8bbb75aSkrw 	xs->cmdlen = sizeof(*cmd);
281f8bbb75aSkrw 	xs->data = buf;
282f8bbb75aSkrw 	xs->datalen = SES_BUFLEN;
283f8bbb75aSkrw 	xs->retries = 2;
284f8bbb75aSkrw 	xs->timeout = 3000;
285cc96c0e3Sderaadt 
286664c6166Skrw 	cmd = (struct ses_scsi_diag *)&xs->cmd;
28749227f1aSmatthew 	cmd->opcode = RECEIVE_DIAGNOSTIC;
288dacf4336Skrw 	SET(cmd->flags, SES_DIAG_PCV);
289f8bbb75aSkrw 	cmd->pgcode = SES_PAGE_CONFIG;
290f8bbb75aSkrw 	cmd->length = htobe16(SES_BUFLEN);
291f8bbb75aSkrw 
292f8bbb75aSkrw 	error = scsi_xs_sync(xs);
293f8bbb75aSkrw 	scsi_xs_put(xs);
294f8bbb75aSkrw 
295f8bbb75aSkrw 	if (error) {
2965c0b007dSderaadt 		error = 1;
2975c0b007dSderaadt 		goto done;
298d506901aSdlg 	}
299d506901aSdlg 
300d506901aSdlg 	cfg = (struct ses_config_hdr *)buf;
30113c2458dSkrw 	if (cfg->pgcode != SES_PAGE_CONFIG || betoh16(cfg->length) >
302f8bbb75aSkrw 	    SES_BUFLEN) {
3035c0b007dSderaadt 		error = 1;
3045c0b007dSderaadt 		goto done;
305d506901aSdlg 	}
306d506901aSdlg 
307d506901aSdlg 	DPRINTF("%s: config: n_subenc: %d length: %d\n", DEVNAME(sc),
308d506901aSdlg 	    cfg->n_subenc, betoh16(cfg->length));
309d506901aSdlg 
310d506901aSdlg 	p = buf + SES_CFG_HDRLEN;
311d506901aSdlg 	for (i = 0; i <= cfg->n_subenc; i++) {
312d506901aSdlg 		enc = (struct ses_enc_hdr *)p;
313d506901aSdlg #ifdef SES_DEBUG
314d506901aSdlg 		DPRINTF("%s: enclosure %d enc_id: 0x%02x n_types: %d\n",
315d506901aSdlg 		    DEVNAME(sc), i, enc->enc_id, enc->n_types);
316d506901aSdlg 		desc = (struct ses_enc_desc *)(p + SES_ENC_HDRLEN);
317d506901aSdlg 		ses_dump_enc_desc(desc);
318d506901aSdlg #endif /* SES_DEBUG */
319d506901aSdlg 
320d506901aSdlg 		ntypes += enc->n_types;
321d506901aSdlg 
322d506901aSdlg 		p += SES_ENC_HDRLEN + enc->vendor_len;
323d506901aSdlg 	}
324d506901aSdlg 
32543b5650cSkrw 	tdlist = (struct ses_type_desc *)p; /* Stash this for later. */
326d506901aSdlg 
327d506901aSdlg 	for (i = 0; i < ntypes; i++) {
328d506901aSdlg 		tdh = (struct ses_type_desc *)p;
329d506901aSdlg 		DPRINTF("%s: td %d subenc_id: %d type 0x%02x n_elem: %d\n",
330d506901aSdlg 		    DEVNAME(sc), i, tdh->subenc_id, tdh->type, tdh->n_elem);
331d506901aSdlg 
332d506901aSdlg 		nelems += tdh->n_elem;
333d506901aSdlg 
334d506901aSdlg 		p += SES_TYPE_DESCLEN;
335d506901aSdlg 	}
336d506901aSdlg 
337d506901aSdlg #ifdef SES_DEBUG
338d506901aSdlg 	for (i = 0; i < ntypes; i++) {
339d506901aSdlg 		DPRINTF("%s: td %d '%s'\n", DEVNAME(sc), i,
340d506901aSdlg 		    ses_dump_enc_string(p, tdlist[i].desc_len));
341d506901aSdlg 
342d506901aSdlg 		p += tdlist[i].desc_len;
343d506901aSdlg 	}
344d506901aSdlg #endif /* SES_DEBUG */
345d506901aSdlg 
346d506901aSdlg 	sc->sc_buflen = SES_STAT_LEN(ntypes, nelems);
3475c0b007dSderaadt 	sc->sc_buf = dma_alloc(sc->sc_buflen, PR_NOWAIT);
348d506901aSdlg 	if (sc->sc_buf == NULL) {
3495c0b007dSderaadt 		error = 1;
3505c0b007dSderaadt 		goto done;
351d506901aSdlg 	}
352d506901aSdlg 
35343b5650cSkrw 	/* Get the status page and then use it to generate a list of sensors. */
354d506901aSdlg 	if (ses_make_sensors(sc, tdlist, ntypes) != 0) {
3555c0b007dSderaadt 		dma_free(sc->sc_buf, sc->sc_buflen);
3565c0b007dSderaadt 		error = 1;
3575c0b007dSderaadt 		goto done;
358d506901aSdlg 	}
359d506901aSdlg 
3605c0b007dSderaadt done:
3615c0b007dSderaadt 	if (buf)
3625c0b007dSderaadt 		dma_free(buf, SES_BUFLEN);
36343b5650cSkrw 	return error;
364d506901aSdlg }
365d506901aSdlg 
366d506901aSdlg int
ses_read_status(struct ses_softc * sc)3677351cb62Smarco ses_read_status(struct ses_softc *sc)
368d506901aSdlg {
369f8bbb75aSkrw 	struct ses_scsi_diag		*cmd;
370f8bbb75aSkrw 	struct scsi_xfer		*xs;
371f8bbb75aSkrw 	int				 error, flags = 0;
372d506901aSdlg 
3737351cb62Smarco 	if (cold)
374dacf4336Skrw 		SET(flags, SCSI_AUTOCONF);
375f8bbb75aSkrw 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_IN | SCSI_SILENT);
376f8bbb75aSkrw 	if (xs == NULL)
37743b5650cSkrw 		return 1;
378f8bbb75aSkrw 	xs->cmdlen = sizeof(*cmd);
379f8bbb75aSkrw 	xs->data = sc->sc_buf;
380f8bbb75aSkrw 	xs->datalen = sc->sc_buflen;
381f8bbb75aSkrw 	xs->retries = 2;
382f8bbb75aSkrw 	xs->timeout = 3000;
383d506901aSdlg 
384664c6166Skrw 	cmd = (struct ses_scsi_diag *)&xs->cmd;
38549227f1aSmatthew 	cmd->opcode = RECEIVE_DIAGNOSTIC;
386dacf4336Skrw 	SET(cmd->flags, SES_DIAG_PCV);
387f8bbb75aSkrw 	cmd->pgcode = SES_PAGE_STATUS;
388f8bbb75aSkrw 	cmd->length = htobe16(sc->sc_buflen);
389f8bbb75aSkrw 
390f8bbb75aSkrw 	error = scsi_xs_sync(xs);
391f8bbb75aSkrw 	scsi_xs_put(xs);
392f8bbb75aSkrw 
393f8bbb75aSkrw 	if (error != 0)
39443b5650cSkrw 		return 1;
395d506901aSdlg 
39643b5650cSkrw 	return 0;
397d506901aSdlg }
398d506901aSdlg 
399d506901aSdlg int
ses_make_sensors(struct ses_softc * sc,struct ses_type_desc * types,int ntypes)400d506901aSdlg ses_make_sensors(struct ses_softc *sc, struct ses_type_desc *types, int ntypes)
401d506901aSdlg {
402d506901aSdlg 	struct ses_status		*status;
403d506901aSdlg 	struct ses_sensor		*sensor;
40443b5650cSkrw 	char				*fmt;
4056e1ed140Sdlg #if NBIO > 0
4066e1ed140Sdlg 	struct ses_slot			*slot;
407364ebb70Skrw #endif /* NBIO > 0 */
408d506901aSdlg 	enum sensor_type		 stype;
409d506901aSdlg 	int				 i, j;
410d506901aSdlg 
4117351cb62Smarco 	if (ses_read_status(sc) != 0)
41243b5650cSkrw 		return 1;
413d506901aSdlg 
41427515a6bSderaadt 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
41527515a6bSderaadt 	    sizeof(sc->sc_sensordev.xname));
416b206af6eSdlg 
417d506901aSdlg 	TAILQ_INIT(&sc->sc_sensors);
4186e1ed140Sdlg #if NBIO > 0
4196e1ed140Sdlg 	TAILQ_INIT(&sc->sc_slots);
420364ebb70Skrw #endif /* NBIO > 0 */
421d506901aSdlg 
422d506901aSdlg 	status = (struct ses_status *)(sc->sc_buf + SES_STAT_HDRLEN);
423d506901aSdlg 	for (i = 0; i < ntypes; i++) {
42443b5650cSkrw 		/* Ignore the overall status element for this type. */
425d506901aSdlg 		DPRINTFN(1, "%s: %3d:-   0x%02x 0x%02x%02x%02x type: 0x%02x\n",
426d506901aSdlg 		     DEVNAME(sc), i, status->com, status->f1, status->f2,
427d506901aSdlg 		    status->f3, types[i].type);
428d506901aSdlg 
429d506901aSdlg 		for (j = 0; j < types[i].n_elem; j++) {
43043b5650cSkrw 			/* Move to the current status element. */
431d506901aSdlg 			status++;
432d506901aSdlg 
433d506901aSdlg 			DPRINTFN(1, "%s: %3d:%-3d 0x%02x 0x%02x%02x%02x\n",
434d506901aSdlg 			    DEVNAME(sc), i, j, status->com, status->f1,
435d506901aSdlg 			    status->f2, status->f3);
436d506901aSdlg 
437d506901aSdlg 			if (SES_STAT_CODE(status->com) == SES_STAT_CODE_NOTINST)
438d506901aSdlg 				continue;
439d506901aSdlg 
440d506901aSdlg 			switch (types[i].type) {
4416e1ed140Sdlg #if NBIO > 0
4426e1ed140Sdlg 			case SES_T_DEVICE:
4436f976379Skrw 				slot = malloc(sizeof(*slot), M_DEVBUF,
4446f976379Skrw 				    M_NOWAIT | M_ZERO);
4456e1ed140Sdlg 				if (slot == NULL)
4466e1ed140Sdlg 					goto error;
4476e1ed140Sdlg 
4486e1ed140Sdlg 				slot->sl_stat = status;
4496e1ed140Sdlg 
4506e1ed140Sdlg 				TAILQ_INSERT_TAIL(&sc->sc_slots, slot,
4516e1ed140Sdlg 				    sl_entry);
4526e1ed140Sdlg 
4536e1ed140Sdlg 				continue;
454364ebb70Skrw #endif /* NBIO > 0 */
4556e1ed140Sdlg 
4567289499aSdlg 			case SES_T_POWERSUPPLY:
4577289499aSdlg 				stype = SENSOR_INDICATOR;
45827515a6bSderaadt 				fmt = "PSU";
4597289499aSdlg 				break;
4607289499aSdlg 
461d7944f47Sdlg 			case SES_T_COOLING:
462cdaf7095Sdlg 				stype = SENSOR_PERCENT;
46327515a6bSderaadt 				fmt = "Fan";
464d7944f47Sdlg 				break;
465d7944f47Sdlg 
466d506901aSdlg 			case SES_T_TEMP:
467d506901aSdlg 				stype = SENSOR_TEMP;
46827515a6bSderaadt 				fmt = "";
469d506901aSdlg 				break;
470d506901aSdlg 
471d506901aSdlg 			default:
472d506901aSdlg 				continue;
473d506901aSdlg 			}
474d506901aSdlg 
4756f976379Skrw 			sensor = malloc(sizeof(*sensor), M_DEVBUF,
4764e32cb41Skrw 			    M_NOWAIT | M_ZERO);
477d506901aSdlg 			if (sensor == NULL)
478d506901aSdlg 				goto error;
479d506901aSdlg 
480d506901aSdlg 			sensor->se_type = types[i].type;
481d506901aSdlg 			sensor->se_stat = status;
482d506901aSdlg 			sensor->se_sensor.type = stype;
48327515a6bSderaadt 			strlcpy(sensor->se_sensor.desc, fmt,
48427515a6bSderaadt 			    sizeof(sensor->se_sensor.desc));
485d506901aSdlg 
486d506901aSdlg 			TAILQ_INSERT_TAIL(&sc->sc_sensors, sensor, se_entry);
487d506901aSdlg 		}
488d506901aSdlg 
48943b5650cSkrw 		/* Move to the overall status element of the next type. */
490d506901aSdlg 		status++;
491d506901aSdlg 	}
492d506901aSdlg 
49343b5650cSkrw 	return 0;
494d506901aSdlg error:
4956e1ed140Sdlg #if NBIO > 0
4966e1ed140Sdlg 	while (!TAILQ_EMPTY(&sc->sc_slots)) {
4976e1ed140Sdlg 		slot = TAILQ_FIRST(&sc->sc_slots);
4986e1ed140Sdlg 		TAILQ_REMOVE(&sc->sc_slots, slot, sl_entry);
499a67129dbStedu 		free(slot, M_DEVBUF, sizeof(*slot));
5006e1ed140Sdlg 	}
501364ebb70Skrw #endif /* NBIO > 0 */
502d506901aSdlg 	while (!TAILQ_EMPTY(&sc->sc_sensors)) {
503d506901aSdlg 		sensor = TAILQ_FIRST(&sc->sc_sensors);
504d506901aSdlg 		TAILQ_REMOVE(&sc->sc_sensors, sensor, se_entry);
505a67129dbStedu 		free(sensor, M_DEVBUF, sizeof(*sensor));
506d506901aSdlg 	}
50743b5650cSkrw 	return 1;
508d506901aSdlg }
509d506901aSdlg 
5108503befaSdlg void
ses_refresh_sensors(void * arg)5118503befaSdlg ses_refresh_sensors(void *arg)
512d506901aSdlg {
5138503befaSdlg 	struct ses_softc		*sc = (struct ses_softc *)arg;
514d506901aSdlg 	struct ses_sensor		*sensor;
515d506901aSdlg 	int				 ret = 0;
516d506901aSdlg 
51734e1a95bSkrw 	rw_enter_write(&sc->sc_lock);
518cc62d9f9Sdlg 
5198503befaSdlg 	if (ses_read_status(sc) != 0) {
52034e1a95bSkrw 		rw_exit_write(&sc->sc_lock);
5218503befaSdlg 		return;
5228503befaSdlg 	}
523d506901aSdlg 
524d506901aSdlg 	TAILQ_FOREACH(sensor, &sc->sc_sensors, se_entry) {
525d506901aSdlg 		DPRINTFN(10, "%s: %s 0x%02x 0x%02x%02x%02x\n", DEVNAME(sc),
526d506901aSdlg 		    sensor->se_sensor.desc, sensor->se_stat->com,
527d506901aSdlg 		    sensor->se_stat->f1, sensor->se_stat->f2,
528d506901aSdlg 		    sensor->se_stat->f3);
529d506901aSdlg 
53052e3dee2Sdlg 		switch (SES_STAT_CODE(sensor->se_stat->com)) {
53152e3dee2Sdlg 		case SES_STAT_CODE_OK:
53252e3dee2Sdlg 			sensor->se_sensor.status = SENSOR_S_OK;
53352e3dee2Sdlg 			break;
53452e3dee2Sdlg 
53552e3dee2Sdlg 		case SES_STAT_CODE_CRIT:
53652e3dee2Sdlg 		case SES_STAT_CODE_UNREC:
53752e3dee2Sdlg 			sensor->se_sensor.status = SENSOR_S_CRIT;
53852e3dee2Sdlg 			break;
53952e3dee2Sdlg 
54052e3dee2Sdlg 		case SES_STAT_CODE_NONCRIT:
54152e3dee2Sdlg 			sensor->se_sensor.status = SENSOR_S_WARN;
54252e3dee2Sdlg 			break;
54352e3dee2Sdlg 
54452e3dee2Sdlg 		case SES_STAT_CODE_NOTINST:
54552e3dee2Sdlg 		case SES_STAT_CODE_UNKNOWN:
54652e3dee2Sdlg 		case SES_STAT_CODE_NOTAVAIL:
54752e3dee2Sdlg 			sensor->se_sensor.status = SENSOR_S_UNKNOWN;
54852e3dee2Sdlg 			break;
54952e3dee2Sdlg 		}
55052e3dee2Sdlg 
551d506901aSdlg 		switch (sensor->se_type) {
5527289499aSdlg 		case SES_T_POWERSUPPLY:
553d99c3651Sdlg 			ses_psu2sensor(sc, sensor);
5547289499aSdlg 			break;
5557289499aSdlg 
556d7944f47Sdlg 		case SES_T_COOLING:
557d99c3651Sdlg 			ses_cool2sensor(sc, sensor);
558d7944f47Sdlg 			break;
559d7944f47Sdlg 
560d506901aSdlg 		case SES_T_TEMP:
561d99c3651Sdlg 			ses_temp2sensor(sc, sensor);
562d506901aSdlg 			break;
563d506901aSdlg 
564d506901aSdlg 		default:
565d506901aSdlg 			ret = 1;
566d506901aSdlg 			break;
567d506901aSdlg 		}
568d506901aSdlg 	}
569d506901aSdlg 
57034e1a95bSkrw 	rw_exit_write(&sc->sc_lock);
571cc62d9f9Sdlg 
5728503befaSdlg 	if (ret)
573ccb87389Smiod 		printf("%s: error in sensor data\n", DEVNAME(sc));
574d506901aSdlg }
575d506901aSdlg 
5766e1ed140Sdlg #if NBIO > 0
5776e1ed140Sdlg int
ses_ioctl(struct device * dev,u_long cmd,caddr_t addr)5786e1ed140Sdlg ses_ioctl(struct device *dev, u_long cmd, caddr_t addr)
5796e1ed140Sdlg {
5806e1ed140Sdlg 	struct ses_softc		*sc = (struct ses_softc *)dev;
5816e1ed140Sdlg 	int				 error = 0;
5826e1ed140Sdlg 
5836e1ed140Sdlg 	switch (cmd) {
5846e1ed140Sdlg 	case BIOCBLINK:
5856e1ed140Sdlg 		error = ses_bio_blink(sc, (struct bioc_blink *)addr);
5866e1ed140Sdlg 		break;
5876e1ed140Sdlg 
5886e1ed140Sdlg 	default:
5896e1ed140Sdlg 		error = EINVAL;
5906e1ed140Sdlg 		break;
5916e1ed140Sdlg 	}
5926e1ed140Sdlg 
59343b5650cSkrw 	return error;
5946e1ed140Sdlg }
5956e1ed140Sdlg 
5966e1ed140Sdlg int
ses_write_config(struct ses_softc * sc)5976e1ed140Sdlg ses_write_config(struct ses_softc *sc)
5986e1ed140Sdlg {
599f8bbb75aSkrw 	struct ses_scsi_diag		*cmd;
600f8bbb75aSkrw 	struct scsi_xfer		*xs;
601f8bbb75aSkrw 	int				 error, flags = 0;
6026e1ed140Sdlg 
6037351cb62Smarco 	if (cold)
604dacf4336Skrw 		SET(flags, SCSI_AUTOCONF);
6057351cb62Smarco 
606f8bbb75aSkrw 	xs = scsi_xs_get(sc->sc_link, flags | SCSI_DATA_OUT | SCSI_SILENT);
607f8bbb75aSkrw 	if (xs == NULL)
60843b5650cSkrw 		return 1;
609f8bbb75aSkrw 	xs->cmdlen = sizeof(*cmd);
610f8bbb75aSkrw 	xs->data = sc->sc_buf;
611f8bbb75aSkrw 	xs->datalen = sc->sc_buflen;
612f8bbb75aSkrw 	xs->retries = 2;
613f8bbb75aSkrw 	xs->timeout = 3000;
614f8bbb75aSkrw 
615664c6166Skrw 	cmd = (struct ses_scsi_diag *)&xs->cmd;
61649227f1aSmatthew 	cmd->opcode = SEND_DIAGNOSTIC;
617dacf4336Skrw 	SET(cmd->flags, SES_DIAG_PF);
618f8bbb75aSkrw 	cmd->length = htobe16(sc->sc_buflen);
619f8bbb75aSkrw 
620f8bbb75aSkrw 	error = scsi_xs_sync(xs);
621f8bbb75aSkrw 	scsi_xs_put(xs);
622f8bbb75aSkrw 
623f8bbb75aSkrw 	if (error != 0)
62443b5650cSkrw 		return 1;
6256e1ed140Sdlg 
62643b5650cSkrw 	return 0;
6276e1ed140Sdlg }
6286e1ed140Sdlg 
6296e1ed140Sdlg int
ses_bio_blink(struct ses_softc * sc,struct bioc_blink * blink)6306e1ed140Sdlg ses_bio_blink(struct ses_softc *sc, struct bioc_blink *blink)
6316e1ed140Sdlg {
6326e1ed140Sdlg 	struct ses_slot			*slot;
6336e1ed140Sdlg 
63434e1a95bSkrw 	rw_enter_write(&sc->sc_lock);
635cc62d9f9Sdlg 
636cc62d9f9Sdlg 	if (ses_read_status(sc) != 0) {
63734e1a95bSkrw 		rw_exit_write(&sc->sc_lock);
63843b5650cSkrw 		return EIO;
639cc62d9f9Sdlg 	}
6406e1ed140Sdlg 
6416e1ed140Sdlg 	TAILQ_FOREACH(slot, &sc->sc_slots, sl_entry) {
6425265ee1dSdlg 		if (slot->sl_stat->f1 == blink->bb_target)
6436e1ed140Sdlg 			break;
6446e1ed140Sdlg 	}
6456e1ed140Sdlg 
646abcbcc4dSdoug 	if (slot == NULL) {
64734e1a95bSkrw 		rw_exit_write(&sc->sc_lock);
64843b5650cSkrw 		return EINVAL;
649cc62d9f9Sdlg 	}
6506e1ed140Sdlg 
6515265ee1dSdlg 	DPRINTFN(3, "%s: 0x%02x 0x%02x 0x%02x 0x%02x\n", DEVNAME(sc),
6525265ee1dSdlg 	    slot->sl_stat->com, slot->sl_stat->f1, slot->sl_stat->f2,
6535265ee1dSdlg 	    slot->sl_stat->f3);
6546e1ed140Sdlg 
655e4b2d639Smarco 	slot->sl_stat->com = SES_STAT_SELECT;
6565265ee1dSdlg 	slot->sl_stat->f2 &= SES_C_DEV_F2MASK;
6575265ee1dSdlg 	slot->sl_stat->f3 &= SES_C_DEV_F3MASK;
658e4b2d639Smarco 
6596e1ed140Sdlg 	switch (blink->bb_status) {
6606e1ed140Sdlg 	case BIOC_SBUNBLINK:
6615265ee1dSdlg 		slot->sl_stat->f2 &= ~SES_C_DEV_IDENT;
6626e1ed140Sdlg 		break;
6636e1ed140Sdlg 
6646e1ed140Sdlg 	case BIOC_SBBLINK:
665dacf4336Skrw 		SET(slot->sl_stat->f2, SES_C_DEV_IDENT);
6666e1ed140Sdlg 		break;
6676e1ed140Sdlg 
6686e1ed140Sdlg 	default:
66934e1a95bSkrw 		rw_exit_write(&sc->sc_lock);
67043b5650cSkrw 		return EINVAL;
6716e1ed140Sdlg 	}
6726e1ed140Sdlg 
6735265ee1dSdlg 	DPRINTFN(3, "%s: 0x%02x 0x%02x 0x%02x 0x%02x\n", DEVNAME(sc),
6745265ee1dSdlg 	    slot->sl_stat->com, slot->sl_stat->f1, slot->sl_stat->f2,
6755265ee1dSdlg 	    slot->sl_stat->f3);
6765265ee1dSdlg 
677cc62d9f9Sdlg 	if (ses_write_config(sc) != 0) {
67834e1a95bSkrw 		rw_exit_write(&sc->sc_lock);
67943b5650cSkrw 		return EIO;
680cc62d9f9Sdlg 	}
681cc62d9f9Sdlg 
68234e1a95bSkrw 	rw_exit_write(&sc->sc_lock);
683e4b2d639Smarco 
68443b5650cSkrw 	return 0;
6856e1ed140Sdlg }
686364ebb70Skrw #endif /* NBIO > 0 */
6876e1ed140Sdlg 
68852e3dee2Sdlg void
ses_psu2sensor(struct ses_softc * sc,struct ses_sensor * s)689d99c3651Sdlg ses_psu2sensor(struct ses_softc *sc, struct ses_sensor *s)
6907289499aSdlg {
6917289499aSdlg 	s->se_sensor.value = SES_S_PSU_OFF(s->se_stat) ? 0 : 1;
6927289499aSdlg }
6937289499aSdlg 
6947289499aSdlg void
ses_cool2sensor(struct ses_softc * sc,struct ses_sensor * s)695d99c3651Sdlg ses_cool2sensor(struct ses_softc *sc, struct ses_sensor *s)
696d7944f47Sdlg {
697d99c3651Sdlg 	switch (sc->sc_enctype) {
698d99c3651Sdlg 	case SES_ENC_STD:
699cdaf7095Sdlg 		switch (SES_S_COOL_CODE(s->se_stat)) {
700cdaf7095Sdlg 		case SES_S_COOL_C_STOPPED:
701cdaf7095Sdlg 			s->se_sensor.value = 0;
702cdaf7095Sdlg 			break;
703cdaf7095Sdlg 		case SES_S_COOL_C_LOW1:
704cdaf7095Sdlg 		case SES_S_COOL_C_LOW2:
705cdaf7095Sdlg 		case SES_S_COOL_C_LOW3:
706d99c3651Sdlg 			s->se_sensor.value = 33333;
707cdaf7095Sdlg 			break;
708cdaf7095Sdlg 		case SES_S_COOL_C_INTER:
709cdaf7095Sdlg 		case SES_S_COOL_C_HI3:
710cdaf7095Sdlg 		case SES_S_COOL_C_HI2:
711d99c3651Sdlg 			s->se_sensor.value = 66666;
712cdaf7095Sdlg 			break;
713cdaf7095Sdlg 		case SES_S_COOL_C_HI1:
714d99c3651Sdlg 			s->se_sensor.value = 100000;
715d99c3651Sdlg 			break;
716d99c3651Sdlg 		}
717d99c3651Sdlg 		break;
718d99c3651Sdlg 
719d99c3651Sdlg 	/* Dell only use the first three codes to represent speed */
720d99c3651Sdlg 	case SES_ENC_DELL:
721d99c3651Sdlg 		switch (SES_S_COOL_CODE(s->se_stat)) {
722d99c3651Sdlg 		case SES_S_COOL_C_STOPPED:
723d99c3651Sdlg 			s->se_sensor.value = 0;
724d99c3651Sdlg 			break;
725d99c3651Sdlg 		case SES_S_COOL_C_LOW1:
726d99c3651Sdlg 			s->se_sensor.value = 33333;
727d99c3651Sdlg 			break;
728d99c3651Sdlg 		case SES_S_COOL_C_LOW2:
729d99c3651Sdlg 			s->se_sensor.value = 66666;
730d99c3651Sdlg 			break;
731d99c3651Sdlg 		case SES_S_COOL_C_LOW3:
732d99c3651Sdlg 		case SES_S_COOL_C_INTER:
733d99c3651Sdlg 		case SES_S_COOL_C_HI3:
734d99c3651Sdlg 		case SES_S_COOL_C_HI2:
735d99c3651Sdlg 		case SES_S_COOL_C_HI1:
736d99c3651Sdlg 			s->se_sensor.value = 100000;
737d99c3651Sdlg 			break;
738d99c3651Sdlg 		}
739cdaf7095Sdlg 		break;
740cdaf7095Sdlg 	}
741d7944f47Sdlg }
742d7944f47Sdlg 
74352e3dee2Sdlg void
ses_temp2sensor(struct ses_softc * sc,struct ses_sensor * s)744d99c3651Sdlg ses_temp2sensor(struct ses_softc *sc, struct ses_sensor *s)
745d506901aSdlg {
74652e3dee2Sdlg 	s->se_sensor.value = (int64_t)SES_S_TEMP(s->se_stat);
74752e3dee2Sdlg 	s->se_sensor.value += SES_S_TEMP_OFFSET;
74843b5650cSkrw 	s->se_sensor.value *= 1000000;		/* Convert to micro degrees. */
74943b5650cSkrw 	s->se_sensor.value += 273150000;	/* Convert to kelvin. */
750d506901aSdlg }
751d506901aSdlg 
752d506901aSdlg #ifdef SES_DEBUG
753d506901aSdlg void
ses_dump_enc_desc(struct ses_enc_desc * desc)754d506901aSdlg ses_dump_enc_desc(struct ses_enc_desc *desc)
755d506901aSdlg {
756d506901aSdlg 	char				str[32];
757d506901aSdlg 
758d506901aSdlg #if 0
759d506901aSdlg 	/* XXX not a string. wwn? */
760d506901aSdlg 	memset(str, 0, sizeof(str));
761d506901aSdlg 	memcpy(str, desc->logical_id, sizeof(desc->logical_id));
762d506901aSdlg 	DPRINTF("logical_id: %s", str);
763364ebb70Skrw #endif /* 0 */
764d506901aSdlg 
765d506901aSdlg 	memset(str, 0, sizeof(str));
766d506901aSdlg 	memcpy(str, desc->vendor_id, sizeof(desc->vendor_id));
767d506901aSdlg 	DPRINTF(" vendor_id: %s", str);
768d506901aSdlg 
769d506901aSdlg 	memset(str, 0, sizeof(str));
770d506901aSdlg 	memcpy(str, desc->prod_id, sizeof(desc->prod_id));
771d506901aSdlg 	DPRINTF(" prod_id: %s", str);
772d506901aSdlg 
773d506901aSdlg 	memset(str, 0, sizeof(str));
774d506901aSdlg 	memcpy(str, desc->prod_rev, sizeof(desc->prod_rev));
775d506901aSdlg 	DPRINTF(" prod_rev: %s\n", str);
776d506901aSdlg }
777d506901aSdlg 
778d506901aSdlg char *
ses_dump_enc_string(u_char * buf,ssize_t len)779d506901aSdlg ses_dump_enc_string(u_char *buf, ssize_t len)
780d506901aSdlg {
781d506901aSdlg 	static char			str[256];
782d506901aSdlg 
783d506901aSdlg 	memset(str, 0, sizeof(str));
784d506901aSdlg 	if (len > 0)
785d506901aSdlg 		memcpy(str, buf, len);
786d506901aSdlg 
78743b5650cSkrw 	return str;
788d506901aSdlg }
789d506901aSdlg #endif /* SES_DEBUG */
790