Update: merged 3 changes from Jonathan Matthew.  Other things remain.  See
[1] for details.

- Don't check threashold in this driver; fix false positive warnings.
- Handle disabled sensors, but allow them to supply readings if they want.
- Make command taskq MP-safe.

Masao

[1] https://github.com/uebayasi/openbsd-ipmi

Index: sys/arch/amd64/amd64/conf.c
===================================================================
RCS file: /cvs/src/sys/arch/amd64/amd64/conf.c,v
retrieving revision 1.52
diff -u -p -r1.52 conf.c
--- sys/arch/amd64/amd64/conf.c 13 Nov 2015 07:52:20 -0000      1.52
+++ sys/arch/amd64/amd64/conf.c 18 Dec 2015 12:07:54 -0000
@@ -182,6 +182,7 @@ cdev_decl(pci);
 #include "vscsi.h"
 #include "pppx.h"
 #include "fuse.h"
+#include "ipmi.h"
 
 struct cdevsw  cdevsw[] =
 {
@@ -291,6 +292,7 @@ struct cdevsw       cdevsw[] =
        cdev_pppx_init(NPPPX,pppx),     /* 91: pppx */
        cdev_fuse_init(NFUSE,fuse),     /* 92: fuse */
        cdev_tun_init(NTUN,tap),        /* 93: Ethernet network tunnel */
+       cdev_ipmi_init(NIPMI,ipmi),     /* 94: ipmi */
 };
 int    nchrdev = nitems(cdevsw);
 
Index: sys/dev/ipmi.c
===================================================================
RCS file: /cvs/src/sys/dev/ipmi.c,v
retrieving revision 1.77
diff -u -p -r1.77 ipmi.c
--- sys/dev/ipmi.c      8 Sep 2015 11:58:58 -0000       1.77
+++ sys/dev/ipmi.c      18 Dec 2015 12:07:54 -0000
@@ -31,20 +31,21 @@
 #include <sys/systm.h>
 #include <sys/kernel.h>
 #include <sys/device.h>
+#include <sys/ioctl.h>
 #include <sys/extent.h>
-#include <sys/timeout.h>
 #include <sys/sensors.h>
 #include <sys/malloc.h>
 #include <sys/kthread.h>
+#include <sys/task.h>
 
 #include <machine/bus.h>
-#include <machine/intr.h>
 #include <machine/smbiosvar.h>
 
 #include <dev/isa/isareg.h>
 #include <dev/isa/isavar.h>
 
 #include <dev/ipmivar.h>
+#include <dev/ipmi_linux.h>
 
 struct ipmi_sensor {
        u_int8_t        *i_sdr;
@@ -55,8 +56,12 @@ struct ipmi_sensor {
        SLIST_ENTRY(ipmi_sensor) list;
 };
 
-int    ipmi_nintr;
-int    ipmi_poll = 1;
+struct ipmi_stats {
+       int ncmds;
+       int ntickles;
+       int nioctls;
+} ipmi_stats;
+
 int    ipmi_enabled = 0;
 
 #define SENSOR_REFRESH_RATE (5 * hz)
@@ -112,6 +117,7 @@ int ipmi_enabled = 0;
 #define IPMI_ENTITY_PWRSUPPLY          0x0A
 
 #define IPMI_INVALID_SENSOR            (1L << 5)
+#define IPMI_DISABLED_SENSOR           (1L << 6)
 
 #define IPMI_SDR_TYPEFULL              1
 #define IPMI_SDR_TYPECOMPACT           2
@@ -139,8 +145,6 @@ SLIST_HEAD(ipmi_sensors_head, ipmi_senso
 struct ipmi_sensors_head ipmi_sensor_list =
     SLIST_HEAD_INITIALIZER(ipmi_sensor_list);
 
-struct timeout ipmi_timeout;
-
 void   dumpb(const char *, int, const u_int8_t *);
 
 int    read_sensor(struct ipmi_softc *, struct ipmi_sensor *);
@@ -149,16 +153,25 @@ int       get_sdr_partial(struct ipmi_softc *,
            u_int8_t, u_int8_t, void *, u_int16_t *);
 int    get_sdr(struct ipmi_softc *, u_int16_t, u_int16_t *);
 
-int    ipmi_sendcmd(struct ipmi_softc *, int, int, int, int, int, const void*);
-int    ipmi_recvcmd(struct ipmi_softc *, int, int *, void *);
-void   ipmi_delay(struct ipmi_softc *, int);
+int    ipmi_sendcmd(struct ipmi_cmd *);
+int    ipmi_recvcmd(struct ipmi_cmd *);
+void   ipmi_cmd(struct ipmi_cmd *);
+void   ipmi_cmd_poll(struct ipmi_cmd *);
+void   ipmi_cmd_wait(struct ipmi_cmd *);
+void   ipmi_cmd_wait_cb(void *);
 
 int    ipmi_watchdog(void *, int);
+void   ipmi_watchdog_tickle(void *);
+void   ipmi_watchdog_set(void *);
 
-int    ipmi_intr(void *);
 int    ipmi_match(struct device *, void *, void *);
 void   ipmi_attach(struct device *, struct device *, void *);
 int    ipmi_activate(struct device *, int);
+struct ipmi_softc *ipmilookup(dev_t dev);
+
+int    ipmiopen(dev_t, int, int, struct proc *);
+int    ipmiclose(dev_t, int, int, struct proc *);
+int    ipmiioctl(dev_t, u_long, caddr_t, int, struct proc *);
 
 long   ipow(long, int);
 long   ipmi_convert(u_int8_t, struct sdrtype1 *, long);
@@ -167,13 +180,10 @@ void      ipmi_sensor_name(char *, int, u_int
 /* BMC Helper Functions */
 u_int8_t bmc_read(struct ipmi_softc *, int);
 void   bmc_write(struct ipmi_softc *, int, u_int8_t);
-int    bmc_io_wait(struct ipmi_softc *, int, u_int8_t, u_int8_t, const char *);
-int    bmc_io_wait_cold(struct ipmi_softc *, int, u_int8_t, u_int8_t,
-    const char *);
-void   _bmc_io_wait(void *);
+int    bmc_io_wait(struct ipmi_softc *);
 
-void   *bt_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
-void   *cmn_buildmsg(struct ipmi_softc *, int, int, int, const void *, int *);
+void   bt_buildmsg(struct ipmi_cmd *);
+void   cmn_buildmsg(struct ipmi_cmd *);
 
 int    getbits(u_int8_t *, int, int);
 int    ipmi_sensor_type(int, int, int);
@@ -185,7 +195,6 @@ void        ipmi_unmap_regs(struct ipmi_softc *
 
 void   *scan_sig(long, long, int, int, const void *);
 
-int    ipmi_test_threshold(u_int8_t, u_int8_t, u_int8_t, u_int8_t, int);
 int    ipmi_sensor_status(struct ipmi_softc *, struct ipmi_sensor *,
     u_int8_t *);
 
@@ -200,6 +209,8 @@ struct ipmi_if kcs_if = {
        kcs_recvmsg,
        kcs_reset,
        kcs_probe,
+       IPMI_MSG_DATASND,
+       IPMI_MSG_DATARCV,
 };
 
 struct ipmi_if smic_if = {
@@ -210,6 +221,8 @@ struct ipmi_if smic_if = {
        smic_recvmsg,
        smic_reset,
        smic_probe,
+       IPMI_MSG_DATASND,
+       IPMI_MSG_DATARCV,
 };
 
 struct ipmi_if bt_if = {
@@ -220,6 +233,8 @@ struct ipmi_if bt_if = {
        bt_recvmsg,
        bt_reset,
        bt_probe,
+       IPMI_BTMSG_DATASND,
+       IPMI_BTMSG_DATARCV,
 };
 
 struct ipmi_if *ipmi_get_if(int);
@@ -256,78 +271,23 @@ bmc_write(struct ipmi_softc *sc, int off
            offset * sc->sc_if_iospacing, val);
 }
 
-void
-_bmc_io_wait(void *arg)
-{
-       struct ipmi_softc       *sc = arg;
-       struct ipmi_bmc_args    *a = sc->sc_iowait_args;
-
-       *a->v = bmc_read(sc, a->offset);
-       if ((*a->v & a->mask) == a->value) {
-               sc->sc_wakeup = 0;
-               wakeup(sc);
-               return;
-       }
-
-       if (++sc->sc_retries > sc->sc_max_retries) {
-               sc->sc_wakeup = 0;
-               wakeup(sc);
-               return;
-       }
-
-       timeout_add(&sc->sc_timeout, 1);
-}
-
 int
-bmc_io_wait(struct ipmi_softc *sc, int offset, u_int8_t mask, u_int8_t value,
-    const char *lbl)
-{
-       volatile u_int8_t       v;
-       struct ipmi_bmc_args    args;
-
-       if (cold || sc->sc_poll)
-               return (bmc_io_wait_cold(sc, offset, mask, value, lbl));
-
-       sc->sc_retries = 0;
-       sc->sc_wakeup = 1;
-
-       args.offset = offset;
-       args.mask = mask;
-       args.value = value;
-       args.v = &v;
-       sc->sc_iowait_args = &args;
-
-       _bmc_io_wait(sc);
-
-       while (sc->sc_wakeup)
-               tsleep(sc, PWAIT, lbl, 0);
-
-       if (sc->sc_retries > sc->sc_max_retries) {
-               dbg_printf(1, "%s: bmc_io_wait fails : v=%.2x m=%.2x "
-                   "b=%.2x %s\n", DEVNAME(sc), v, mask, value, lbl);
-               return (-1);
-       }
-
-       return (v);
-}
-
-int
-bmc_io_wait_cold(struct ipmi_softc *sc, int offset, u_int8_t mask,
-    u_int8_t value, const char *lbl)
+bmc_io_wait(struct ipmi_softc *sc)
 {
+       struct ipmi_iowait      *a = sc->sc_cmd_iowait;
        volatile u_int8_t       v;
        int                     count = 5000000; /* == 5s XXX can be shorter */
 
        while (count--) {
-               v = bmc_read(sc, offset);
-               if ((v & mask) == value)
+               v = bmc_read(sc, a->offset);
+               if ((v & a->mask) == a->value)
                        return v;
 
                delay(1);
        }
 
-       dbg_printf(1, "%s: bmc_io_wait_cold fails : *v=%.2x m=%.2x b=%.2x %s\n",
-           DEVNAME(sc), v, mask, value, lbl);
+       dbg_printf(1, "%s: bmc_io_wait fails : *v=%.2x m=%.2x b=%.2x %s\n",
+           DEVNAME(sc), v, a->mask, a->value, a->lbl);
        return (-1);
 
 }
@@ -369,7 +329,13 @@ bt_read(struct ipmi_softc *sc, int reg)
 int
 bt_write(struct ipmi_softc *sc, int reg, uint8_t data)
 {
-       if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC_BUSY, 0, "bt_write") < 0)
+       struct ipmi_iowait *a = sc->sc_cmd_iowait;
+
+       a->offset = _BT_CTRL_REG;
+       a->mask = BT_BMC_BUSY;
+       a->value = 0;
+       a->lbl = "bt_write";
+       if (bmc_io_wait(sc) < 0)
                return (-1);
 
        bmc_write(sc, reg, data);
@@ -377,42 +343,52 @@ bt_write(struct ipmi_softc *sc, int reg,
 }
 
 int
-bt_sendmsg(struct ipmi_softc *sc, int len, const u_int8_t *data)
+bt_sendmsg(struct ipmi_cmd *c)
 {
+       struct ipmi_softc *sc = c->c_sc;
+       struct ipmi_iowait *a = sc->sc_cmd_iowait;
        int i;
 
        bt_write(sc, _BT_CTRL_REG, BT_CLR_WR_PTR);
-       for (i = 0; i < len; i++)
-               bt_write(sc, _BT_DATAOUT_REG, data[i]);
+       for (i = 0; i < c->c_txlen; i++)
+               bt_write(sc, _BT_DATAOUT_REG, sc->sc_buf[i]);
 
        bt_write(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN);
-       if (bmc_io_wait(sc, _BT_CTRL_REG, BT_HOST2BMC_ATN | BT_BMC_BUSY, 0,
-           "bt_sendwait") < 0)
+       a->offset = _BT_CTRL_REG;
+       a->mask = BT_HOST2BMC_ATN | BT_BMC_BUSY;
+       a->value = 0;
+       a->lbl = "bt_sendwait";
+       if (bmc_io_wait(sc) < 0)
                return (-1);
 
        return (0);
 }
 
 int
-bt_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, u_int8_t *data)
+bt_recvmsg(struct ipmi_cmd *c)
 {
-       u_int8_t len, v, i;
-
-       if (bmc_io_wait(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN, BT_BMC2HOST_ATN,
-           "bt_recvwait") < 0)
+       struct ipmi_softc *sc = c->c_sc;
+       struct ipmi_iowait *a = sc->sc_cmd_iowait;
+       u_int8_t len, v, i, j;
+
+       a->offset = _BT_CTRL_REG;
+       a->mask = BT_BMC2HOST_ATN;
+       a->value = BT_BMC2HOST_ATN;
+       a->lbl = "bt_recvwait";
+       if (bmc_io_wait(sc) < 0)
                return (-1);
 
        bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
        bt_write(sc, _BT_CTRL_REG, BT_BMC2HOST_ATN);
        bt_write(sc, _BT_CTRL_REG, BT_CLR_RD_PTR);
        len = bt_read(sc, _BT_DATAIN_REG);
-       for (i = IPMI_BTMSG_NFLN; i <= len; i++) {
+       for (i = IPMI_BTMSG_NFLN, j = 0; i <= len; i++) {
                v = bt_read(sc, _BT_DATAIN_REG);
                if (i != IPMI_BTMSG_SEQ)
-                       *(data++) = v;
+                       *(sc->sc_buf + j++) = v;
        }
        bt_write(sc, _BT_CTRL_REG, BT_HOST_BUSY);
-       *rxlen = len - 1;
+       c->c_rxlen = len - 1;
 
        return (0);
 }
@@ -490,10 +466,15 @@ int       smic_read_data(struct ipmi_softc *, 
 int
 smic_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t val, const char *lbl)
 {
+       struct ipmi_iowait *a = sc->sc_cmd_iowait;
        int v;
 
        /* Wait for expected flag bits */
-       v = bmc_io_wait(sc, _SMIC_FLAG_REG, mask, val, "smicwait");
+       a->offset = _SMIC_FLAG_REG;
+       a->mask = mask;
+       a->value = val;
+       a->lbl = "smicwait";
+       v = bmc_io_wait(sc);
        if (v < 0)
                return (-1);
 
@@ -542,20 +523,21 @@ smic_read_data(struct ipmi_softc *sc, u_
 #define ErrStat(a,b) if (a) printf(b);
 
 int
-smic_sendmsg(struct ipmi_softc *sc, int len, const u_int8_t *data)
+smic_sendmsg(struct ipmi_cmd *c)
 {
+       struct ipmi_softc *sc = c->c_sc;
        int sts, idx;
 
-       sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &data[0]);
+       sts = smic_write_cmd_data(sc, SMS_CC_START_TRANSFER, &sc->sc_buf[0]);
        ErrStat(sts != SMS_SC_WRITE_START, "wstart");
-       for (idx = 1; idx < len - 1; idx++) {
+       for (idx = 1; idx < c->c_txlen - 1; idx++) {
                sts = smic_write_cmd_data(sc, SMS_CC_NEXT_TRANSFER,
-                   &data[idx]);
+                   &sc->sc_buf[idx]);
                ErrStat(sts != SMS_SC_WRITE_NEXT, "write");
        }
-       sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &data[idx]);
+       sts = smic_write_cmd_data(sc, SMS_CC_END_TRANSFER, &sc->sc_buf[idx]);
        if (sts != SMS_SC_WRITE_END) {
-               dbg_printf(50, "smic_sendmsg %d/%d = %.2x\n", idx, len, sts);
+               dbg_printf(50, "smic_sendmsg %d/%d = %.2x\n", idx, c->c_txlen, 
sts);
                return (-1);
        }
 
@@ -563,11 +545,12 @@ smic_sendmsg(struct ipmi_softc *sc, int 
 }
 
 int
-smic_recvmsg(struct ipmi_softc *sc, int maxlen, int *len, u_int8_t *data)
+smic_recvmsg(struct ipmi_cmd *c)
 {
+       struct ipmi_softc *sc = c->c_sc;
        int sts, idx;
 
-       *len = 0;
+       c->c_rxlen = 0;
        sts = smic_wait(sc, SMIC_RX_DATA_RDY, SMIC_RX_DATA_RDY, "smic_recvmsg");
        if (sts < 0)
                return (-1);
@@ -575,18 +558,18 @@ smic_recvmsg(struct ipmi_softc *sc, int 
        sts = smic_write_cmd_data(sc, SMS_CC_START_RECEIVE, NULL);
        ErrStat(sts != SMS_SC_READ_START, "rstart");
        for (idx = 0;; ) {
-               sts = smic_read_data(sc, &data[idx++]);
+               sts = smic_read_data(sc, &sc->sc_buf[idx++]);
                if (sts != SMS_SC_READ_START && sts != SMS_SC_READ_NEXT)
                        break;
                smic_write_cmd_data(sc, SMS_CC_NEXT_RECEIVE, NULL);
        }
        ErrStat(sts != SMS_SC_READ_END, "rend");
 
-       *len = idx;
+       c->c_rxlen = idx;
 
        sts = smic_write_cmd_data(sc, SMS_CC_END_RECEIVE, NULL);
        if (sts != SMS_SC_READY) {
-               dbg_printf(50, "smic_recvmsg %d/%d = %.2x\n", idx, maxlen, sts);
+               dbg_printf(50, "smic_recvmsg %d/%d = %.2x\n", idx, 
c->c_maxrxlen, sts);
                return (-1);
        }
 
@@ -642,9 +625,14 @@ int        kcs_read_data(struct ipmi_softc *, u
 int
 kcs_wait(struct ipmi_softc *sc, u_int8_t mask, u_int8_t value, const char *lbl)
 {
+       struct ipmi_iowait *a = sc->sc_cmd_iowait;
        int v;
 
-       v = bmc_io_wait(sc, _KCS_STATUS_REGISTER, mask, value, lbl);
+       a->offset = _KCS_STATUS_REGISTER;
+       a->mask = mask;
+       a->value = value;
+       a->lbl = lbl;
+       v = bmc_io_wait(sc);
        if (v < 0)
                return (v);
 
@@ -704,25 +692,26 @@ kcs_read_data(struct ipmi_softc *sc, u_i
 
 /* Exported KCS functions */
 int
-kcs_sendmsg(struct ipmi_softc *sc, int len, const u_int8_t * data)
+kcs_sendmsg(struct ipmi_cmd *c)
 {
+       struct ipmi_softc *sc = c->c_sc;
        int idx, sts;
 
        /* ASSERT: IBF is clear */
-       dbg_dump(50, "kcs sendmsg", len, data);
+       dbg_dump(50, "kcs sendmsg", c->c_txlen, sc->sc_buf);
        sts = kcs_write_cmd(sc, KCS_WRITE_START);
-       for (idx = 0; idx < len; idx++) {
-               if (idx == len - 1)
+       for (idx = 0; idx < c->c_txlen; idx++) {
+               if (idx == c->c_txlen - 1)
                        sts = kcs_write_cmd(sc, KCS_WRITE_END);
 
                if (sts != KCS_WRITE_STATE)
                        break;
 
-               sts = kcs_write_data(sc, data[idx]);
+               sts = kcs_write_data(sc, sc->sc_buf[idx]);
        }
        if (sts != KCS_READ_STATE) {
-               dbg_printf(1, "kcs sendmsg = %d/%d <%.2x>\n", idx, len, sts);
-               dbg_dump(1, "kcs_sendmsg", len, data);
+               dbg_printf(1, "kcs sendmsg = %d/%d <%.2x>\n", idx, c->c_txlen, 
sts);
+               dbg_dump(1, "kcs_sendmsg", c->c_txlen, sc->sc_buf);
                return (-1);
        }
 
@@ -730,23 +719,24 @@ kcs_sendmsg(struct ipmi_softc *sc, int l
 }
 
 int
-kcs_recvmsg(struct ipmi_softc *sc, int maxlen, int *rxlen, u_int8_t * data)
+kcs_recvmsg(struct ipmi_cmd *c)
 {
+       struct ipmi_softc *sc = c->c_sc;
        int idx, sts;
 
-       for (idx = 0; idx < maxlen; idx++) {
-               sts = kcs_read_data(sc, &data[idx]);
+       for (idx = 0; idx < c->c_maxrxlen; idx++) {
+               sts = kcs_read_data(sc, &sc->sc_buf[idx]);
                if (sts != KCS_READ_STATE)
                        break;
        }
        sts = kcs_wait(sc, KCS_IBF, 0, "recv");
-       *rxlen = idx;
+       c->c_rxlen = idx;
        if (sts != KCS_IDLE_STATE) {
-               dbg_printf(1, "kcs recvmsg = %d/%d <%.2x>\n", idx, maxlen, sts);
+               dbg_printf(1, "kcs recvmsg = %d/%d <%.2x>\n", idx, 
c->c_maxrxlen, sts);
                return (-1);
        }
 
-       dbg_dump(50, "kcs recvmsg", idx, data);
+       dbg_dump(50, "kcs recvmsg", idx, sc->sc_buf);
 
        return (0);
 }
@@ -935,74 +925,54 @@ ipmi_smbios_probe(struct smbios_ipmi *pi
 /*
  * bt_buildmsg builds an IPMI message from a nfLun, cmd, and data
  * This is used by BT protocol
- *
- * Returns a buffer to an allocated message, txlen contains length
- *   of allocated message
  */
-void *
-bt_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len,
-    const void *data, int *txlen)
+void
+bt_buildmsg(struct ipmi_cmd *c)
 {
-       u_int8_t *buf;
+       struct ipmi_softc *sc = c->c_sc;
+       u_int8_t *buf = sc->sc_buf;
+       int nfLun = NETFN_LUN(c->c_netfn, c->c_rslun);
 
        /* Block transfer needs 4 extra bytes: length/netfn/seq/cmd + data */
-       *txlen = len + 4;
-       buf = malloc(*txlen, M_DEVBUF, M_NOWAIT);
-       if (buf == NULL)
-               return (NULL);
-
-       buf[IPMI_BTMSG_LEN] = len + 3;
+       buf[IPMI_BTMSG_LEN] = c->c_txlen + (IPMI_BTMSG_DATASND - 1);
        buf[IPMI_BTMSG_NFLN] = nfLun;
        buf[IPMI_BTMSG_SEQ] = sc->sc_btseq++;
-       buf[IPMI_BTMSG_CMD] = cmd;
-       if (len && data)
-               memcpy(buf + IPMI_BTMSG_DATASND, data, len);
-
-       return (buf);
+       buf[IPMI_BTMSG_CMD] = c->c_cmd;
+       if (c->c_txlen && c->c_data)
+               memcpy(buf + IPMI_BTMSG_DATASND, c->c_data, c->c_txlen);
 }
 
 /*
  * cmn_buildmsg builds an IPMI message from a nfLun, cmd, and data
  * This is used by both SMIC and KCS protocols
- *
- * Returns a buffer to an allocated message, txlen contains length
- *   of allocated message
  */
-void *
-cmn_buildmsg(struct ipmi_softc *sc, int nfLun, int cmd, int len,
-    const void *data, int *txlen)
+void
+cmn_buildmsg(struct ipmi_cmd *c)
 {
-       u_int8_t *buf;
+       struct ipmi_softc *sc = c->c_sc;
+       u_int8_t *buf = sc->sc_buf;
+       int nfLun = NETFN_LUN(c->c_netfn, c->c_rslun);
 
        /* Common needs two extra bytes: nfLun/cmd + data */
-       *txlen = len + 2;
-       buf = malloc(*txlen, M_DEVBUF, M_NOWAIT);
-       if (buf == NULL)
-               return (NULL);
-
        buf[IPMI_MSG_NFLN] = nfLun;
-       buf[IPMI_MSG_CMD] = cmd;
-       if (len && data)
-               memcpy(buf + IPMI_MSG_DATASND, data, len);
-
-       return (buf);
+       buf[IPMI_MSG_CMD] = c->c_cmd;
+       if (c->c_txlen && c->c_data)
+               memcpy(buf + IPMI_MSG_DATASND, c->c_data, c->c_txlen);
 }
 
 /* Send an IPMI command */
 int
-ipmi_sendcmd(struct ipmi_softc *sc, int rssa, int rslun, int netfn, int cmd,
-    int txlen, const void *data)
+ipmi_sendcmd(struct ipmi_cmd *c)
 {
-       u_int8_t        *buf;
+       struct ipmi_softc       *sc = c->c_sc;
        int             rc = -1;
 
        dbg_printf(50, "ipmi_sendcmd: rssa=%.2x nfln=%.2x cmd=%.2x len=%.2x\n",
-           rssa, NETFN_LUN(netfn, rslun), cmd, txlen);
-       dbg_dump(10, " send", txlen, data);
-       if (rssa != BMC_SA) {
+           c->c_rssa, NETFN_LUN(c->c_netfn, c->c_rslun), c->c_cmd, c->c_txlen);
+       dbg_dump(10, " send", c->c_txlen, c->c_data);
+       if (c->c_rssa != BMC_SA) {
 #if 0
-               buf = sc->sc_if->buildmsg(sc, NETFN_LUN(APP_NETFN, BMC_LUN),
-                   APP_SEND_MESSAGE, 7 + txlen, NULL, &txlen);
+               sc->sc_if->buildmsg(c);
                pI2C->bus = (sc->if_ver == 0x09) ?
                    PUBLIC_BUS :
                    IPMB_CHANNEL_NUMBER;
@@ -1020,44 +990,32 @@ ipmi_sendcmd(struct ipmi_softc *sc, int 
 #endif
                goto done;
        } else
-               buf = sc->sc_if->buildmsg(sc, NETFN_LUN(netfn, rslun), cmd,
-                   txlen, data, &txlen);
-
-       if (buf == NULL) {
-               printf("%s: sendcmd malloc fails\n", DEVNAME(sc));
-               goto done;
-       }
-       rc = sc->sc_if->sendmsg(sc, txlen, buf);
-       free(buf, M_DEVBUF, txlen);
+               sc->sc_if->buildmsg(c);
 
-       ipmi_delay(sc, 5); /* give bmc chance to digest command */
+       c->c_txlen += sc->sc_if->datasnd;
+       rc = sc->sc_if->sendmsg(c);
 
 done:
        return (rc);
 }
 
+/* Receive an IPMI command */
 int
-ipmi_recvcmd(struct ipmi_softc *sc, int maxlen, int *rxlen, void *data)
+ipmi_recvcmd(struct ipmi_cmd *c)
 {
-       u_int8_t        *buf, rc = 0;
-       int             rawlen;
+       struct ipmi_softc *sc = c->c_sc;
+       u_int8_t        *buf = sc->sc_buf, rc = 0;
 
-       /* Need three extra bytes: netfn/cmd/ccode + data */
-       buf = malloc(maxlen + 3, M_DEVBUF, M_NOWAIT);
-       if (buf == NULL) {
-               printf("%s: ipmi_recvcmd: malloc fails\n", DEVNAME(sc));
-               return (-1);
-       }
        /* Receive message from interface, copy out result data */
-       if (sc->sc_if->recvmsg(sc, maxlen + 3, &rawlen, buf) ||
-           rawlen < IPMI_MSG_DATARCV) {
-               free(buf, M_DEVBUF, maxlen + 3);
+       c->c_maxrxlen += sc->sc_if->datarcv;
+       if (sc->sc_if->recvmsg(c) ||
+           c->c_rxlen < sc->sc_if->datarcv) {
                return (-1);
        }
 
-       *rxlen = rawlen - IPMI_MSG_DATARCV;
-       if (*rxlen > 0 && data)
-               memcpy(data, buf + IPMI_MSG_DATARCV, *rxlen);
+       c->c_rxlen -= sc->sc_if->datarcv;
+       if (c->c_rxlen > 0 && c->c_data)
+               memcpy(c->c_data, buf + sc->sc_if->datarcv, c->c_rxlen);
 
        rc = buf[IPMI_MSG_CCODE];
 #ifdef IPMI_DEBUG
@@ -1068,25 +1026,65 @@ ipmi_recvcmd(struct ipmi_softc *sc, int 
 
        dbg_printf(50, "ipmi_recvcmd: nfln=%.2x cmd=%.2x err=%.2x len=%.2x\n",
            buf[IPMI_MSG_NFLN], buf[IPMI_MSG_CMD], buf[IPMI_MSG_CCODE],
-           *rxlen);
-       dbg_dump(10, " recv", *rxlen, data);
-
-       free(buf, M_DEVBUF, maxlen + 3);
-
-       ipmi_delay(sc, 5); /* give bmc chance to digest command */
+           c->c_rxlen);
+       dbg_dump(10, " recv", c->c_rxlen, c->c_data);
 
        return (rc);
 }
 
 void
-ipmi_delay(struct ipmi_softc *sc, int period)
+ipmi_cmd(struct ipmi_cmd *c)
 {
-       /* period is in 10 ms increments */
-       if (cold || sc->sc_poll)
-               delay(period * 10000);
+       if (cold || panicstr != NULL)
+               ipmi_cmd_poll(c);
        else
-               while (tsleep(sc, PWAIT, "ipmicmd", period) != EWOULDBLOCK)
-                       continue;
+               ipmi_cmd_wait(c);
+}
+
+void
+ipmi_cmd_poll(struct ipmi_cmd *c)
+{
+       struct ipmi_iowait      iowait;
+
+       mtx_enter(&c->c_sc->sc_cmd_mtx);
+
+       c->c_sc->sc_cmd = c;
+       c->c_sc->sc_cmd_iowait = &iowait;
+       if (ipmi_sendcmd(c)) {
+               panic("%s: sendcmd fails", DEVNAME(c->c_sc));
+       }
+       c->c_ccode = ipmi_recvcmd(c);
+       c->c_sc->sc_cmd = NULL;
+       c->c_sc->sc_cmd_iowait = NULL;
+
+       ipmi_stats.ncmds++;
+
+       mtx_leave(&c->c_sc->sc_cmd_mtx);
+}
+
+void
+ipmi_cmd_wait(struct ipmi_cmd *c)
+{
+       struct task t;
+       int res;
+
+       task_set(&t, ipmi_cmd_wait_cb, c);
+       res = task_add(c->c_sc->sc_cmd_taskq, &t);
+       KASSERT(res == 1);
+
+       tsleep(c, PWAIT, "ipmicmd", 0);
+
+       res = task_del(c->c_sc->sc_cmd_taskq, &t);
+       KASSERT(res == 0);
+}
+
+void
+ipmi_cmd_wait_cb(void *arg)
+{
+       struct ipmi_cmd *c = arg;
+
+       ipmi_cmd_poll(c);
+       wakeup(c);
 }
 
 /* Read a partial SDR entry */
@@ -1101,18 +1099,24 @@ get_sdr_partial(struct ipmi_softc *sc, u
        ((u_int16_t *) cmd)[1] = recordId;
        cmd[4] = offset;
        cmd[5] = length;
-       if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_GET_SDR, 6,
-           cmd)) {
-               printf("%s: sendcmd fails\n", DEVNAME(sc));
-               return (-1);
-       }
-       if (ipmi_recvcmd(sc, 8 + length, &len, cmd)) {
-               printf("%s: getSdrPartial: recvcmd fails\n", DEVNAME(sc));
-               return (-1);
-       }
+
+       struct ipmi_cmd c;
+       c.c_sc = sc;
+       c.c_rssa = BMC_SA;
+       c.c_rslun = BMC_LUN;
+       c.c_netfn = STORAGE_NETFN;
+       c.c_cmd = STORAGE_GET_SDR;
+       c.c_txlen = 6;
+       c.c_maxrxlen = 8 + length;
+       c.c_rxlen = 0;
+       c.c_data = cmd;
+       ipmi_cmd(&c);
+       len = c.c_rxlen;
+
        if (nxtRecordId)
                *nxtRecordId = *(uint16_t *) cmd;
-       memcpy(buffer, cmd + 2, len - 2);
+       if (len > 2)
+               memcpy(buffer, cmd + 2, len - 2);
 
        return (0);
 }
@@ -1129,15 +1133,18 @@ get_sdr(struct ipmi_softc *sc, u_int16_t
        struct sdrhdr   shdr;
 
        /* Reserve SDR */
-       if (ipmi_sendcmd(sc, BMC_SA, 0, STORAGE_NETFN, STORAGE_RESERVE_SDR,
-           0, NULL)) {
-               printf("%s: reserve send fails\n", DEVNAME(sc));
-               return (1);
-       }
-       if (ipmi_recvcmd(sc, sizeof(resid), &len, &resid)) {
-               printf("%s: reserve recv fails\n", DEVNAME(sc));
-               return (1);
-       }
+       struct ipmi_cmd c;
+       c.c_sc = sc;
+       c.c_rssa = BMC_SA;
+       c.c_rslun = BMC_LUN;
+       c.c_netfn = STORAGE_NETFN;
+       c.c_cmd = STORAGE_RESERVE_SDR;
+       c.c_txlen = 0;
+       c.c_maxrxlen = sizeof(resid);
+       c.c_rxlen = 0;
+       c.c_data = &resid;
+       ipmi_cmd(&c);
+
        /* Get SDR Header */
        if (get_sdr_partial(sc, recid, resid, 0, sizeof shdr, &shdr, nxtrec)) {
                printf("%s: get header fails\n", DEVNAME(sc));
@@ -1291,26 +1298,11 @@ ipmi_convert(u_int8_t v, struct sdrtype1
 }
 
 int
-ipmi_test_threshold(u_int8_t v, u_int8_t valid, u_int8_t hi, u_int8_t lo,
-    int sign)
-{
-       dbg_printf(10, "thresh: %.2x %.2x %.2x %d %d\n", v, lo, hi,valid, sign);
-       if (sign)
-               return ((valid & 1 && lo != 0x00 && (int8_t)v <= (int8_t)lo) ||
-                   (valid & 8 && hi != 0xFF && (int8_t)v >= (int8_t)hi));
-
-       return ((valid & 1 && lo != 0x00 && v <= lo) ||
-           (valid & 8 && hi != 0xFF && v >= hi));
-}
-
-int
 ipmi_sensor_status(struct ipmi_softc *sc, struct ipmi_sensor *psensor,
     u_int8_t *reading)
 {
-       u_int8_t        data[32];
        struct sdrtype1 *s1 = (struct sdrtype1 *)psensor->i_sdr;
-       int             rxlen, etype;
-       int             sign = s1->units1 >> 7 & 1;
+       int             etype;
 
        /* Get reading of sensor */
        switch (psensor->i_sensor.type) {
@@ -1338,28 +1330,12 @@ ipmi_sensor_status(struct ipmi_softc *sc
        case IPMI_SENSOR_TYPE_TEMP:
        case IPMI_SENSOR_TYPE_VOLT:
        case IPMI_SENSOR_TYPE_FAN:
-               data[0] = psensor->i_num;
-               if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun,
-                   SE_NETFN, SE_GET_SENSOR_THRESHOLD, 1, data) ||
-                   ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
-                       return (SENSOR_S_UNKNOWN);
-
-               dbg_printf(25, "recvdata: %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n",
-                   data[0], data[1], data[2], data[3], data[4], data[5],
-                   data[6]);
-
-               if (ipmi_test_threshold(*reading, data[0] >> 2 ,
-                   data[6], data[3], sign))
+               if (reading[2] & ((1 << 5) | (1 << 2)))
                        return (SENSOR_S_CRIT);
-
-               if (ipmi_test_threshold(*reading, data[0] >> 1,
-                   data[5], data[2], sign))
+               else if (reading[2] & ((1 << 4) | (1 << 1)))
                        return (SENSOR_S_CRIT);
-
-               if (ipmi_test_threshold(*reading, data[0] ,
-                   data[4], data[1], sign))
+               else if (reading[2] & ((1 << 3) | (1 << 0)))
                        return (SENSOR_S_WARN);
-
                break;
 
        case IPMI_SENSOR_TYPE_INTRUSION:
@@ -1394,32 +1370,31 @@ read_sensor(struct ipmi_softc *sc, struc
 {
        struct sdrtype1 *s1 = (struct sdrtype1 *) psensor->i_sdr;
        u_int8_t        data[8];
-       int             rxlen, rv = -1;
-
-       if (!cold)
-               rw_enter_write(&sc->sc_lock);
+       int             rv = -1;
 
        memset(data, 0, sizeof(data));
        data[0] = psensor->i_num;
-       if (ipmi_sendcmd(sc, s1->owner_id, s1->owner_lun, SE_NETFN,
-           SE_GET_SENSOR_READING, 1, data))
-               goto done;
 
-       if (ipmi_recvcmd(sc, sizeof(data), &rxlen, data))
-               goto done;
+       struct ipmi_cmd c;
+       c.c_sc = sc;
+       c.c_rssa = s1->owner_id;
+       c.c_rslun = s1->owner_lun;
+       c.c_netfn = SE_NETFN;
+       c.c_cmd = SE_GET_SENSOR_READING;
+       c.c_txlen = 1;
+       c.c_maxrxlen = sizeof(data);
+       c.c_rxlen = 0;
+       c.c_data = data;
+       ipmi_cmd(&c);
 
        dbg_printf(10, "values=%.2x %.2x %.2x %.2x %s\n",
            data[0],data[1],data[2],data[3], psensor->i_sensor.desc);
        psensor->i_sensor.flags &= ~SENSOR_FINVALID;
-       if (data[1] & IPMI_INVALID_SENSOR) {
-               /* Check if sensor is valid */
+       if ((data[1] & IPMI_INVALID_SENSOR) ||
+           ((data[1] & IPMI_DISABLED_SENSOR) == 0 && data[0] == 0))
                psensor->i_sensor.flags |= SENSOR_FINVALID;
-       }
        psensor->i_sensor.status = ipmi_sensor_status(sc, psensor, data);
        rv = 0;
-done:
-       if (!cold)
-               rw_exit_write(&sc->sc_lock);
        return (rv);
 }
 
@@ -1530,27 +1505,10 @@ add_child_sensors(struct ipmi_softc *sc,
        return (1);
 }
 
-/* Interrupt handler */
-int
-ipmi_intr(void *arg)
-{
-       struct ipmi_softc       *sc = (struct ipmi_softc *)arg;
-       int                     v;
-
-       v = bmc_read(sc, _KCS_STATUS_REGISTER);
-       if (v & KCS_OBF)
-               ++ipmi_nintr;
-
-       return (0);
-}
-
 /* Handle IPMI Timer - reread sensor values */
 void
 ipmi_refresh_sensors(struct ipmi_softc *sc)
 {
-       if (!ipmi_poll)
-               return;
-
        if (SLIST_EMPTY(&ipmi_sensor_list))
                return;
 
@@ -1588,11 +1546,6 @@ ipmi_map_regs(struct ipmi_softc *sc, str
                    sc->sc_if->nregs * sc->sc_if_iospacing, &sc->sc_ioh);
                return (-1);
        }
-#if 0
-       if (iaa->if_if_irq != -1)
-               sc->ih = isa_intr_establish(-1, iaa->if_if_irq,
-                   iaa->if_irqlvl, IPL_BIO, ipmi_intr, sc, DEVNAME(sc));
-#endif
        return (0);
 }
 
@@ -1607,7 +1560,7 @@ void
 ipmi_poll_thread(void *arg)
 {
        struct ipmi_thread      *thread = arg;
-       struct ipmi_softc       *sc = thread->sc;
+       struct ipmi_softc       *sc = thread->sc;
        u_int16_t               rec;
 
        /* Scan SDRs, add sensors */
@@ -1687,7 +1640,6 @@ ipmi_match(struct device *parent, void *
        struct ipmi_attach_args *ia = aux;
        struct cfdata           *cf = match;
        u_int8_t                cmd[32];
-       int                     len;
        int                     rv = 0;
 
        if (strcmp(ia->iaa_name, cf->cf_driver->cd_name))
@@ -1695,24 +1647,26 @@ ipmi_match(struct device *parent, void *
 
        /* XXX local softc is wrong wrong wrong */
        strlcpy(sc.sc_dev.dv_xname, "ipmi0", sizeof(sc.sc_dev.dv_xname));
+       mtx_init(&sc.sc_cmd_mtx, IPL_NONE);
+
        /* Map registers */
        if (ipmi_map_regs(&sc, ia) == 0) {
                sc.sc_if->probe(&sc);
 
                /* Identify BMC device early to detect lying bios */
-               if (ipmi_sendcmd(&sc, BMC_SA, 0, APP_NETFN, APP_GET_DEVICE_ID,
-                   0, NULL)) {
-                       dbg_printf(1, ": unable to send get device id "
-                           "command\n");
-                       goto unmap;
-               }
-               if (ipmi_recvcmd(&sc, sizeof(cmd), &len, cmd)) {
-                       dbg_printf(1, ": unable to retrieve device id\n");
-                       goto unmap;
-               }
+               struct ipmi_cmd c;
+               c.c_sc = &sc;
+               c.c_rssa = BMC_SA;
+               c.c_rslun = BMC_LUN;
+               c.c_netfn = APP_NETFN;
+               c.c_cmd = APP_GET_DEVICE_ID;
+               c.c_txlen = 0;
+               c.c_maxrxlen = sizeof(cmd);
+               c.c_rxlen = 0;
+               c.c_data = cmd;
+               ipmi_cmd(&c);
 
-               dbg_dump(1, "bmc data", len, cmd);
-unmap:
+               dbg_dump(1, "bmc data", c.c_rxlen, cmd);
                rv = 1; /* GETID worked, we got IPMI */
                ipmi_unmap_regs(&sc);
        }
@@ -1725,6 +1679,8 @@ ipmi_attach(struct device *parent, struc
 {
        struct ipmi_softc       *sc = (void *) self;
        struct ipmi_attach_args *ia = aux;
+       struct ipmi_cmd         *c = &sc->sc_ioctl.cmd;
+       int                     i;
 
        /* Map registers */
        ipmi_map_regs(sc, ia);
@@ -1753,16 +1709,27 @@ ipmi_attach(struct device *parent, struc
 
        /* Setup Watchdog timer */
        sc->sc_wdog_period = 0;
+       for (i = 0; i < IPMI_WDOG_TICKLE_NTASKS; i++)
+               task_set(&sc->sc_wdog_tickle_tasks[i], ipmi_watchdog_tickle, 
sc);
+       sc->sc_wdog_tickle_cnt = 0;
        wdog_register(ipmi_watchdog, sc);
 
-       /* lock around read_sensor so that no one messes with the bmc regs */
-       rw_init(&sc->sc_lock, DEVNAME(sc));
+       rw_init(&sc->sc_ioctl.lock, DEVNAME(sc));
+       sc->sc_ioctl.req.msgid = -1;
 
-       /* setup ticker */
-       sc->sc_retries = 0;
-       sc->sc_wakeup = 0;
-       sc->sc_max_retries = 50; /* 50 * 1/100 = 0.5 seconds max */
-       timeout_set(&sc->sc_timeout, _bmc_io_wait, sc);
+       c->c_sc = sc;
+       c->c_rssa = -1;
+       c->c_rslun = -1;
+       c->c_netfn = -1;
+       c->c_cmd = -1;
+       c->c_txlen = -1;
+       c->c_rxlen = -1;
+       c->c_ccode = -1;
+
+       sc->sc_cmd = NULL;
+       sc->sc_cmd_iowait = NULL;
+       sc->sc_cmd_taskq = taskq_create("ipmicmd", 1, IPL_NONE, TASKQ_MPSAFE);
+       mtx_init(&sc->sc_cmd_mtx, IPL_NONE);
 }
 
 int
@@ -1777,52 +1744,251 @@ ipmi_activate(struct device *self, int a
        return (0);
 }
 
+struct ipmi_softc *
+ipmilookup(dev_t dev)
+{
+       return (struct ipmi_softc *)device_lookup(&ipmi_cd, minor(dev));
+}
+
+int
+ipmiopen(dev_t dev, int flags, int mode, struct proc *p)
+{
+       return (0);
+}
+
+int
+ipmiclose(dev_t dev, int flags, int mode, struct proc *p)
+{
+       return (0);
+}
+
+int
+ipmiioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *proc)
+{
+       struct ipmi_softc       *sc = ipmilookup(dev);
+       struct ipmi_req         *req = (struct ipmi_req *)data;
+       struct ipmi_recv        *recv = (struct ipmi_recv *)data;
+       struct ipmi_cmd         *c = &sc->sc_ioctl.cmd;
+       int                     len;
+       u_char                  ccode;
+       int                     rc = 0;
+       uint8_t                 buf[IPMI_MAX_RX];
+
+       if (sc == NULL)
+               return (ENXIO);
+
+#ifdef IPMICTL_SEND_COMMAND_32
+       switch (cmd) {
+       case IPMICTL_SEND_COMMAND_32:
+       case IPMICTL_RECEIVE_MSG_TRUNC_32:
+       case IPMICTL_RECEIVE_MSG_32:
+               break;
+       }
+#endif
+
+       rw_enter_write(&sc->sc_ioctl.lock);
+
+       c->c_maxrxlen = sizeof(buf);
+       c->c_data = buf;
+
+       switch (cmd) {
+#ifdef IPMICTL_SEND_COMMAND_32
+       case IPMICTL_SEND_COMMAND_32:
+#endif
+       case IPMICTL_SEND_COMMAND:
+               if (req->msgid == -1) {
+                       rc = EINVAL; // XXX
+                       goto reset;
+               }
+               if (sc->sc_ioctl.req.msgid != -1) {
+                       rc = EBUSY;
+                       goto reset;
+               }
+               len = req->msg.data_len;
+               if (len > c->c_maxrxlen) {
+                       rc = E2BIG; // XXX
+                       goto reset;
+               }
+               sc->sc_ioctl.req = *req;
+               rc = copyin(req->msg.data, c->c_data, len);
+               if (rc != 0)
+                       goto reset;
+
+               /* Execute a command synchronously. */
+               /* c_sc is constant */
+               /* c_rssa is set by IPMICTL_SET_MY_ADDRESS_CMD */
+               /* c_rslun is set by IPMICTL_SET_MY_LUN_CMD */
+               c->c_netfn = req->msg.netfn;
+               c->c_cmd = req->msg.cmd;
+               c->c_txlen = req->msg.data_len;
+               /* c_maxrxlen is constant */
+               c->c_rxlen = 0;
+               /* c_data is constant */
+               c->c_ccode = -1;
+               ipmi_cmd(c);
+
+               /* c_sc is constant */
+               /* c_rssa is set by IPMICTL_SET_MY_ADDRESS_CMD */
+               /* c_rslun is set by IPMICTL_SET_MY_LUN_CMD */
+               c->c_netfn = -1;
+               c->c_cmd = -1;
+               c->c_txlen = -1;
+               /* c_maxrxlen is constant */
+               /* c_rxlen is saved for IPMICTL_RECEIVE_MSG */
+               /* c_data is constant */
+               /* c_ccode is saved for IPMICTL_RECEIVE_MSG */
+               break;
+#ifdef IPMICTL_SEND_COMMAND_32
+       case IPMICTL_RECEIVE_MSG_TRUNC_32:
+       case IPMICTL_RECEIVE_MSG_32:
+#endif
+       case IPMICTL_RECEIVE_MSG_TRUNC:
+       case IPMICTL_RECEIVE_MSG:
+               if (sc->sc_ioctl.req.msgid == -1) {
+                       rc = EINVAL;
+                       goto reset;
+               }
+               if (c->c_ccode == -1) {
+                       rc = EAGAIN;
+                       goto reset;
+               }
+               ccode = c->c_ccode & 0xff;
+               rc = copyout(&ccode, recv->msg.data, 1);
+
+               /* Return a command result. */
+               recv->recv_type = IPMI_RESPONSE_RECV_TYPE;
+               recv->msgid = sc->sc_ioctl.req.msgid;
+               recv->msg.netfn = sc->sc_ioctl.req.msg.netfn;
+               recv->msg.cmd = sc->sc_ioctl.req.msg.cmd;
+               recv->msg.data_len = c->c_rxlen + 1;
+
+               rc = copyout(c->c_data, recv->msg.data + 1, c->c_rxlen);
+               goto reset;
+       case IPMICTL_SET_MY_ADDRESS_CMD:
+               c->c_rssa = *(int *)data;
+               break;
+       case IPMICTL_GET_MY_ADDRESS_CMD:
+               *(int *)data = c->c_rssa;
+               break;
+       case IPMICTL_SET_MY_LUN_CMD:
+               c->c_rslun = *(int *)data;
+               break;
+       case IPMICTL_GET_MY_LUN_CMD:
+               *(int *)data = c->c_rslun;
+               break;
+       case IPMICTL_SET_GETS_EVENTS_CMD:
+               break;
+       case IPMICTL_REGISTER_FOR_CMD:
+       case IPMICTL_UNREGISTER_FOR_CMD:
+               rc = EOPNOTSUPP;
+               goto done;
+       default:
+               break;
+       }
+done:
+       rw_exit_write(&sc->sc_ioctl.lock);
+       ipmi_stats.nioctls++;
+       return (rc);
+reset:
+       sc->sc_ioctl.req.msgid = -1;
+
+       /* c_sc is constant */
+       /* c_rssa is set by IPMICTL_SET_MY_ADDRESS_CMD */
+       /* c_rslun is set by IPMICTL_SET_MY_LUN_CMD */
+       c->c_netfn = -1;
+       c->c_cmd = -1;
+       c->c_txlen = -1;
+       /* c_maxrxlen is constant */
+       c->c_rxlen = -1;
+       /* c_data is constant */
+       c->c_ccode = -1;
+
+       goto done;
+}
+
+#define                MIN_PERIOD      10
+
 int
 ipmi_watchdog(void *arg, int period)
 {
        struct ipmi_softc       *sc = arg;
-       uint8_t                 wdog[IPMI_GET_WDOG_MAX];
-       int                     s, rc, len;
 
        if (sc->sc_wdog_period == period) {
                if (period != 0) {
-                       s = splsoftclock();
-                       sc->sc_poll = 1;
-                       /* tickle the watchdog */
-                       rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
-                           APP_RESET_WATCHDOG, 0, NULL);
-                       rc = ipmi_recvcmd(sc, 0, &len, NULL);
-                       sc->sc_poll = 0;
-                       splx(s);
+                       struct task *t;
+                       int res;
+
+                       t = &sc->sc_wdog_tickle_tasks[
+                           sc->sc_wdog_tickle_cnt++ % IPMI_WDOG_TICKLE_NTASKS];
+                       res = task_del(systq, t);
+                       KASSERT(res == 0);
+                       res = task_add(systq, t);
+                       KASSERT(res == 1);
                }
                return (period);
        }
+       if (period < MIN_PERIOD && period > 0)
+               period = MIN_PERIOD;
+       sc->sc_wdog_period = period;
+       ipmi_watchdog_set(sc);
+       return (period);
+}
+
+void
+ipmi_watchdog_tickle(void *arg)
+{
+       struct ipmi_softc       *sc = arg;
+       struct ipmi_cmd         c;
 
-       if (period < 10 && period > 0)
-               period = 10;
+       c.c_sc = sc;
+       c.c_rssa = BMC_SA;
+       c.c_rslun = BMC_LUN;
+       c.c_netfn = APP_NETFN;
+       c.c_cmd = APP_RESET_WATCHDOG;
+       c.c_txlen = 0;
+       c.c_maxrxlen = 0;
+       c.c_rxlen = 0;
+       c.c_data = NULL;
+       ipmi_cmd(&c);
 
-       s = splsoftclock();
-       sc->sc_poll = 1;
-       /* XXX what to do if poking wdog fails? */
-       rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
-           APP_GET_WATCHDOG_TIMER, 0, NULL);
-       rc = ipmi_recvcmd(sc, IPMI_GET_WDOG_MAX, &len, wdog);
+       ipmi_stats.ntickles++;
+}
 
-       /* Period is 10ths/sec */
-       uint16_t timo = htole16(period * 10);
+void
+ipmi_watchdog_set(void *arg)
+{
+       struct ipmi_softc       *sc = arg;
+       uint8_t                 wdog[IPMI_GET_WDOG_MAX];
+       struct ipmi_cmd         c;
 
+       c.c_sc = sc;
+       c.c_rssa = BMC_SA;
+       c.c_rslun = BMC_LUN;
+       c.c_netfn = APP_NETFN;
+       c.c_cmd = APP_GET_WATCHDOG_TIMER;
+       c.c_txlen = 0;
+       c.c_maxrxlen = IPMI_GET_WDOG_MAX;
+       c.c_rxlen = 0;
+       c.c_data = wdog;
+       ipmi_cmd(&c);
+
+       wdog[IPMI_SET_WDOG_TIMER] &= ~IPMI_WDOG_TIMER_DONTSTOP;
+       wdog[IPMI_SET_WDOG_TIMER] |= (sc->sc_wdog_period == 0) ?
+           0 : IPMI_WDOG_TIMER_DONTSTOP;
+       wdog[IPMI_SET_WDOG_ACTION] &= ~IPMI_WDOG_ACTION_ACTION_MASK;
+       wdog[IPMI_SET_WDOG_ACTION] |= (sc->sc_wdog_period == 0) ?
+           IPMI_WDOG_ACTION_ACTION_DISABLED : IPMI_WDOG_ACTION_ACTION_REBOOT;
+       /* Period is 10ths/sec */
+       uint16_t timo = htole16(sc->sc_wdog_period * 10);
        memcpy(&wdog[IPMI_SET_WDOG_TIMOL], &timo, 2);
-       wdog[IPMI_SET_WDOG_ACTION] &= ~IPMI_WDOG_MASK;
-       wdog[IPMI_SET_WDOG_ACTION] |= (period == 0) ? IPMI_WDOG_DISABLED :
-           IPMI_WDOG_REBOOT;
-
-       rc = ipmi_sendcmd(sc, BMC_SA, BMC_LUN, APP_NETFN,
-           APP_SET_WATCHDOG_TIMER, IPMI_SET_WDOG_MAX, wdog);
-       rc = ipmi_recvcmd(sc, 0, &len, NULL);
 
-       sc->sc_poll = 0;
-       splx(s);
+       c.c_cmd = APP_SET_WATCHDOG_TIMER;
+       c.c_txlen = IPMI_SET_WDOG_MAX;
+       c.c_maxrxlen = 0;
+       c.c_rxlen = 0;
+       c.c_data = wdog;
+       ipmi_cmd(&c);
 
-       sc->sc_wdog_period = period;
-       return (period);
+       printf("%s: watchdog %sabled\n", DEVNAME(sc),
+           (sc->sc_wdog_period == 0) ? "dis" : "en");
 }
Index: sys/dev/ipmivar.h
===================================================================
RCS file: /cvs/src/sys/dev/ipmivar.h,v
retrieving revision 1.19
diff -u -p -r1.19 ipmivar.h
--- sys/dev/ipmivar.h   7 Jan 2015 07:49:18 -0000       1.19
+++ sys/dev/ipmivar.h   18 Dec 2015 12:07:54 -0000
@@ -30,9 +30,11 @@
 #ifndef _IPMIVAR_H_
 #define _IPMIVAR_H_
 
-#include <sys/timeout.h>
 #include <sys/rwlock.h>
 #include <sys/sensors.h>
+#include <sys/task.h>
+
+#include <dev/ipmi_linux.h>
 
 #define IPMI_IF_KCS            1
 #define IPMI_IF_SMIC           2
@@ -44,12 +46,14 @@
 
 struct ipmi_thread;
 struct ipmi_softc;
+struct ipmi_cmd;
 
-struct ipmi_bmc_args{
+struct ipmi_iowait {
        int                     offset;
        u_int8_t                mask;
        u_int8_t                value;
        volatile u_int8_t       *v;
+       const char              *lbl;
 };
 
 struct ipmi_attach_args {
@@ -69,12 +73,29 @@ struct ipmi_attach_args {
 struct ipmi_if {
        const char      *name;
        int             nregs;
-       void            *(*buildmsg)(struct ipmi_softc *, int, int, int,
-                           const void *, int *);
-       int             (*sendmsg)(struct ipmi_softc *, int, const u_int8_t *);
-       int             (*recvmsg)(struct ipmi_softc *, int, int *, u_int8_t *);
+       void            (*buildmsg)(struct ipmi_cmd *);
+       int             (*sendmsg)(struct ipmi_cmd *);
+       int             (*recvmsg)(struct ipmi_cmd *);
        int             (*reset)(struct ipmi_softc *);
        int             (*probe)(struct ipmi_softc *);
+       int             datasnd;
+       int             datarcv;
+};
+
+struct ipmi_cmd {
+       struct ipmi_softc       *c_sc;
+
+       int                     c_rssa;
+       int                     c_rslun;
+       int                     c_netfn;
+       int                     c_cmd;
+
+       int                     c_txlen;
+       int                     c_maxrxlen;
+       int                     c_rxlen;
+
+       void                    *c_data;
+       u_int                   c_ccode;
 };
 
 struct ipmi_softc {
@@ -89,24 +110,26 @@ struct ipmi_softc {
        bus_space_handle_t      sc_ioh;
 
        int                     sc_btseq;
+       u_int8_t                sc_buf[IPMI_MAX_RX + 16];
+       struct ipmi_cmd         *sc_cmd;
+       struct ipmi_iowait      *sc_cmd_iowait;
+       struct taskq            *sc_cmd_taskq;
+       struct mutex            sc_cmd_mtx;
+
+       struct ipmi_ioctl {
+               struct rwlock           lock;
+               struct ipmi_req         req;
+               struct ipmi_cmd         cmd;
+       } sc_ioctl;
 
        int                     sc_wdog_period;
+#define        IPMI_WDOG_TICKLE_NTASKS 2
+       struct task             sc_wdog_tickle_tasks[IPMI_WDOG_TICKLE_NTASKS];
+       int                     sc_wdog_tickle_cnt;
 
        struct ipmi_thread      *sc_thread;
-
-       struct timeout          sc_timeout;
-       int                     sc_max_retries;
-       int                     sc_retries;
-       int                     sc_wakeup;
-
-       struct rwlock           sc_lock;
-
-       struct ipmi_bmc_args    *sc_iowait_args;
-
        struct ipmi_sensor      *current_sensor;
        struct ksensordev       sc_sensordev;
-
-       int                     sc_poll;
 };
 
 struct ipmi_thread {
@@ -114,11 +137,15 @@ struct ipmi_thread {
        volatile int        running;
 };
 
-#define IPMI_WDOG_MASK         0x03
-#define IPMI_WDOG_DISABLED     0x00
-#define IPMI_WDOG_REBOOT       0x01
-#define IPMI_WDOG_PWROFF       0x02
-#define IPMI_WDOG_PWRCYCLE     0x03
+#define IPMI_WDOG_TIMER_DONTLOG                        0x80
+#define IPMI_WDOG_TIMER_DONTSTOP               0x40
+#define IPMI_WDOG_TIMER_USE_MASK               0x07
+
+#define IPMI_WDOG_ACTION_ACTION_MASK           0x03
+#define IPMI_WDOG_ACTION_ACTION_DISABLED       0x00
+#define IPMI_WDOG_ACTION_ACTION_REBOOT         0x01
+#define IPMI_WDOG_ACTION_ACTION_PWROFF         0x02
+#define IPMI_WDOG_ACTION_ACTION_PWRCYCLE       0x03
 
 #define IPMI_WDOG_PRE_DISABLED 0x00
 #define IPMI_WDOG_PRE_SMI      0x01
@@ -148,18 +175,18 @@ void      ipmi_poll_thread(void *);
 
 int    kcs_probe(struct ipmi_softc *);
 int    kcs_reset(struct ipmi_softc *);
-int    kcs_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
-int    kcs_recvmsg(struct ipmi_softc *, int, int *len, u_int8_t *);
+int    kcs_sendmsg(struct ipmi_cmd *);
+int    kcs_recvmsg(struct ipmi_cmd *);
 
 int    bt_probe(struct ipmi_softc *);
 int    bt_reset(struct ipmi_softc *);
-int    bt_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
-int    bt_recvmsg(struct ipmi_softc *, int, int *, u_int8_t *);
+int    bt_sendmsg(struct ipmi_cmd *);
+int    bt_recvmsg(struct ipmi_cmd *);
 
 int    smic_probe(struct ipmi_softc *);
 int    smic_reset(struct ipmi_softc *);
-int    smic_sendmsg(struct ipmi_softc *, int, const u_int8_t *);
-int    smic_recvmsg(struct ipmi_softc *, int, int *, u_int8_t *);
+int    smic_sendmsg(struct ipmi_cmd *);
+int    smic_recvmsg(struct ipmi_cmd *);
 
 struct dmd_ipmi {
        u_int8_t        dmd_sig[4];             /* Signature 'IPMI' */
Index: sys/sys/conf.h
===================================================================
RCS file: /cvs/src/sys/sys/conf.h,v
retrieving revision 1.135
diff -u -p -r1.135 conf.h
--- sys/sys/conf.h      23 Oct 2015 15:10:52 -0000      1.135
+++ sys/sys/conf.h      18 Dec 2015 12:07:54 -0000
@@ -465,6 +465,13 @@ extern struct cdevsw cdevsw[];
        (dev_type_stop((*))) enodev, 0, dev_init(c,n,poll), \
        (dev_type_mmap((*))) enodev, 0, D_CLONE, dev_init(c,n,kqfilter) }
 
+/* open, close, read, write, poll, ioctl, nokqfilter */
+#define cdev_ipmi_init(c,n) { \
+       dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \
+       (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \
+       (dev_type_stop((*))) enodev, 0, (dev_type_poll((*))) enodev, \
+       (dev_type_mmap((*))) enodev, 0 }
+
 #endif
 
 /*
@@ -596,6 +603,7 @@ cdev_decl(hotplug);
 cdev_decl(gpio);
 cdev_decl(amdmsr);
 cdev_decl(fuse);
+cdev_decl(ipmi);
 
 #endif
 

Reply via email to