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