----- Forwarded message from stan <st...@panix.com> ----- From: stan <st...@panix.com> To: Theo de Raadt <dera...@cvs.openbsd.org> Subject: Re: watchdog suport for new hardware Date: Tue, 26 Apr 2016 09:19:20 -0400 User-Agent: Mutt/1.5.4i X-Operating-System: Debian GNU/Linux X-Kernel-Version: 2.4.23 X-Uptime: 09:17:17 up 91 days, 8:18, 1 user, load average: 0.00, 0.02, 0.04 X-Editor: gVim
Hee is the core of the functionality: /* $OpenBSD: selwd.c,v 1.0 2016/04/01 05:00:00 jsg Exp $ */ /* * Copyright (c) 2016 PREMIER System Integrators * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Schweitzer Engineering Laboratories: SEL-3355 Embedded controller */ #include <sys/types.h> #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <machine/bus.h> #include <dev/isa/isavar.h> #include <dev/isa/selwdreg.h> struct selwd_softc { /* sc_dev must be the first item in the struct */ struct device sc_dev; /* device access through bus space */ bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; }; int selwd_wait(bus_space_tag_t, bus_space_handle_t, bool); /* Autoconfiguration glue */ int selwd_probe(struct device *, void *, void *); void selwd_attach(struct device *, struct device *, void *); int selwd_print(void *, const char *); int selwd_wd_cb(void *, int); /* functions to interact with the controller */ void selwd_write_wdog(bus_space_tag_t, bus_space_handle_t, int); u_int8_t selwd_read_wdog(bus_space_tag_t, bus_space_handle_t); u_int8_t selwd_read_status(bus_space_tag_t, bus_space_handle_t); void selwd_abort(bus_space_tag_t, bus_space_handle_t); u_int32_t selwd_read_boardid(bus_space_tag_t, bus_space_handle_t); u_int32_t selwd_read_mcversion(bus_space_tag_t, bus_space_handle_t); u_int16_t selwd_read_pciboardid(bus_space_tag_t, bus_space_handle_t); u_int8_t selwd_read_ledctl0(bus_space_tag_t, bus_space_handle_t); void selwd_write_ledctl0(bus_space_tag_t, bus_space_handle_t, u_int8_t); u_int8_t selwd_read_ledctl1(bus_space_tag_t, bus_space_handle_t); void selwd_write_ledctl1(bus_space_tag_t, bus_space_handle_t, u_int8_t); u_int8_t selwd_read_miscctl0(bus_space_tag_t, bus_space_handle_t); u_int8_t selwd_read_miscctl1(bus_space_tag_t, bus_space_handle_t); void selwd_read_modelno(bus_space_tag_t, bus_space_handle_t, char*); void selwd_read_serialno(bus_space_tag_t, bus_space_handle_t, char*); void selwd_read_configid(bus_space_tag_t, bus_space_handle_t, char*); /* macros to extract bits from the status register */ #define EC_STATUS_IBF(status) (((status) >> 0x1) & 0x1) #define EC_STATUS_OBF(status) (((status) & 0x1)) #define EC_STATUS_BUSY(status) (((status) >> 0x4) & 0x1) #define EC_STATUS_STATE(status) (((status) >> 0x6) & 0x3) struct cfattach selwd_ca = { sizeof(struct selwd_softc), selwd_probe, selwd_attach }; struct cfdriver selwd_cd = { NULL, "selwd", DV_DULL }; const char* selwd_models[] = { SELWD_DEV_3355, SELWD_DEV_3360 }; int selwd_wait(bus_space_tag_t iot, bus_space_handle_t ioh, bool useobf) { uint32_t timeout = 0; uint8_t status = 0; while (timeout < 50) { status = bus_space_read_1(iot, ioh, SELWD_CMD); if ((EC_STATUS_IBF(status) == 0x0) && (EC_STATUS_BUSY(status) == 0x0) && (EC_STATUS_STATE(status) != 0x3) && ((EC_STATUS_OBF(status) == 0x0) || !useobf)) { return 1; } delay(10); ++timeout; } return 0; } static __inline void selwd_conf_enable(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t cmd) { /* write the desired command to the command register */ bus_space_write_1(iot, ioh, SELWD_CMD, cmd); /* wait for controller to be ready */ selwd_wait(iot,ioh,0); } static __inline u_int8_t selwd_conf_read(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t target) { /* write the target address to the data register */ bus_space_write_1(iot, ioh, SELWD_DATA, target); /* wait for controller to be ready */ selwd_wait(iot, ioh,1); /* return the value from the data register */ return (bus_space_read_1(iot, ioh, SELWD_DATA)); } static __inline void selwd_conf_write(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t target, u_int8_t data) { /* write the target address to the data register */ bus_space_write_1(iot, ioh, SELWD_DATA, target); /* wait for controller to be ready */ selwd_wait(iot, ioh,0); /* write the desired data to the data register */ bus_space_write_1(iot, ioh, SELWD_DATA, data); /* wait for controller to be ready */ selwd_wait(iot, ioh,0); } int selwd_probe(struct device *parent, void *match, void *aux) { struct isa_attach_args *ia = aux; bus_space_tag_t iot; bus_space_handle_t ioh; /* Match by device ID */ iot = ia->ia_iot; if (bus_space_map(iot, ia->ipa_io[0].base, SELWD_IOSIZE, 0, &ioh)) return 0; /* print some diagnostic stuff */ struct device *g1parent = parent->dv_parent; struct device *g2parent = g1parent->dv_parent; struct device *g3parent = g2parent->dv_parent; struct device *g4parent = g3parent->dv_parent; if (g4parent == NULL) printf("SEL device: parent=%s, g1parent=%s, g2parent=%s, g3parent=%s, g4parent=NULL\n",parent->dv_xname,g1parent->dv_xname,g2parent->dv_xname,g3parent->dv_xname); else printf("SEL device: parent=%s, g1parent=%s, g2parent=%s, g3parent=%s, g4parent=%s\n",parent->dv_xname,g1parent->dv_xname,g2parent->dv_xname,g3parent->dv_xname,g4parent->dv_xname); /* read model number */ char *model = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO); selwd_read_modelno(iot, ioh, model); bus_space_unmap(iot, ioh, SELWD_IOSIZE); /* model number must match pre-defined values */ int i; for (i=0; selwd_models[i]; i++) { if (!strcmp(model,selwd_models[i])) { ia->ipa_nio = 1; ia->ipa_io[0].length = SELWD_IOSIZE; ia->ipa_nmem = 0; ia->ipa_nirq = 0; ia->ipa_ndrq = 0; return 1; } i++; } free(model, M_DEVBUF, 0); return 0; } void selwd_attach(struct device *parent, struct device *self, void *aux) { struct selwd_softc *sc = (struct selwd_softc *)self; struct isa_attach_args *ia = aux; struct isa_attach_args nia; u_int32_t devid; /* Map ISA I/O space */ sc->sc_iot = ia->ia_iot; if (bus_space_map(sc->sc_iot, ia->ipa_io[0].base, SELWD_IOSIZE, 0, &sc->sc_ioh)) { printf("SEL Watchdog cannot map i/o space\n"); return; } /* read board ID */ devid = selwd_read_boardid(sc->sc_iot, sc->sc_ioh); printf(": SEL Board ID (%x)",devid); /* read model number */ char *model = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO); selwd_read_modelno(sc->sc_iot, sc->sc_ioh, model); printf(", Model No (%s)",model); free(model, M_DEVBUF, 0); /* read serial number */ char *serial = malloc(sizeof(char)*16, M_DEVBUF, M_WAITOK | M_ZERO); selwd_read_serialno(sc->sc_iot, sc->sc_ioh, serial); printf(", Serial No (%s)\n",serial); free(serial, M_DEVBUF, 0); /* write to Enabled and Alarm LEDs */ /* turns Enabled green and Alarm off */ u_int8_t led0 = 0x02; selwd_write_ledctl0(sc->sc_iot, sc->sc_ioh, led0); /* write to Aux LEDs */ /* turns off the LEDs */ u_int8_t led1 = 0x00; selwd_write_ledctl1(sc->sc_iot, sc->sc_ioh, led1); nia = *ia; nia.ia_iobase = 0x192; nia.ia_aux = (void *)(u_long)devid; /* pass devid down to wb_match */ config_found(self, &nia, selwd_print); wdog_register(selwd_wd_cb,sc); } int selwd_wd_cb(void *arg, int period) { /* This function is called by the watchdog daemon */ /* Get a reference to the software context */ struct selwd_softc *sc = arg; if (period < 0) period = 0; /* The period value is in seconds, the watchdog value is in twos-of-secs */ int wd = period / 2; /* write watchdog value */ selwd_write_wdog(sc->sc_iot, sc->sc_ioh, wd); /* return the period back to the daemon */ return period; } int selwd_print(void *aux, const char *pnp) { struct isa_attach_args *ia = aux; if (pnp) printf("%s", pnp); if (ia->ia_iosize) printf(" port 0x%x", ia->ia_iobase); if (ia->ia_iosize > 1) printf("/%d", ia->ia_iosize); printf("\n"); return 0; } void selwd_write_wdog(bus_space_tag_t iot, bus_space_handle_t ioh, int value) { /* Check to make sure that the state is idle */ selwd_abort(iot, ioh); /* Write watchdog timer value */ /* write the WRITECFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG); /* write the watchdog value to the watchdog address */ selwd_conf_write(iot, ioh, SELWD_CFG_WATCHDOG, value); } u_int8_t selwd_read_wdog(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the watchdog value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t wdog; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the WATCHDOG data value from the data register */ wdog = selwd_conf_read(iot, ioh, SELWD_CFG_WATCHDOG); return wdog; } u_int8_t selwd_read_status(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the command/status register */ u_int8_t status = 0; status = bus_space_read_1(iot, ioh, SELWD_CMD); return status; } void selwd_abort(bus_space_tag_t iot, bus_space_handle_t ioh) { /* write the abort value to the command/status register */ u_int8_t status = 0; /* first read the status register */ status = bus_space_read_1(iot, ioh, SELWD_CMD); /* if the state is IDLE return */ if (EC_STATUS_STATE(status) == 0) return; uint32_t timeout = 0; /* make sure status is 0 before proceeding */ while (EC_STATUS_STATE(status) != 0 && timeout < 50) { /* write the abort command */ bus_space_write_1(iot, ioh, SELWD_CMD, SELWD_CMD_ABORT); /* wait until controller isn't busy */ selwd_wait(iot, ioh, 0); /* read the status register */ status = bus_space_read_1(iot, ioh, SELWD_CMD); delay(10); /* increment timeout */ ++timeout; } } u_int32_t selwd_read_boardid(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the board ID from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg0, reg1, reg2, reg3; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the BOARID0 data value from the data register */ reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID0); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the BOARID1 data value from the data register */ reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID1); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the BOARID2 data value from the data register */ reg2 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID2); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the BOARID3 data value from the data register */ reg3 = selwd_conf_read(iot, ioh, SELWD_CFG_BOARDID3); /* convert the 4 bytes into a 32 bit number */ u_int32_t boardid = (reg3 << 24) | (reg2 << 16) | (reg1 << 8) | reg0; return boardid; } u_int32_t selwd_read_mcversion(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the MC version from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg0, reg1, reg2, reg3; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the VERSION0 data value from the data register */ reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION0); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the VERSION1 data value from the data register */ reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION1); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the VERSION2 data value from the data register */ reg2 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION2); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the VERSION3 data value from the data register */ reg3 = selwd_conf_read(iot, ioh, SELWD_CFG_VERSION3); /* convert the 4 bytes into a 32 bit number */ u_int32_t version = (reg3 << 24) | (reg2 << 16) | (reg1 << 8) | reg0; return version; } u_int16_t selwd_read_pciboardid(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the PCI board ID from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg0, reg1; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the PCIBRDID0 data value from the data register */ reg0 = selwd_conf_read(iot, ioh, SELWD_CFG_PCIBRDID0); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the PCIBRDID1 data value from the data register */ reg1 = selwd_conf_read(iot, ioh, SELWD_CFG_PCIBRDID1); /* convert the 2 bytes into a 16 bit number */ u_int16_t boardid = (reg1 << 8) | reg0; return boardid; } u_int8_t selwd_read_miscctl0(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the misc control 0 value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the MISCCTL0 data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_MISCCTL0); return reg; } u_int8_t selwd_read_miscctl1(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the misc control 1 value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the MISCCTL1 data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_MISCCTL1); return reg; } u_int8_t selwd_read_ledctl0(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the LED control 0 value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the LEDCTRL0 data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_LEDCTRL0); return reg; } void selwd_write_ledctl0(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t data) { /* write the LED control 0 value to the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); /* write the WRITECFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG); /* write the LEDCTRL0 data value to the data register */ selwd_conf_write(iot, ioh, SELWD_CFG_LEDCTRL0, data); } u_int8_t selwd_read_ledctl1(bus_space_tag_t iot, bus_space_handle_t ioh) { /* read the LED control 1 value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the LEDCTRL1 data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_LEDCTRL1); return reg; } void selwd_write_ledctl1(bus_space_tag_t iot, bus_space_handle_t ioh, u_int8_t data) { /* write the LED control 1 value to the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); /* write the WRITECFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_WRITECFG); /* write the LEDCTRL1 data value to the data register */ selwd_conf_write(iot, ioh, SELWD_CFG_LEDCTRL1, data); } void selwd_read_modelno(bus_space_tag_t iot, bus_space_handle_t ioh, char* model) { /* read the MODEL NUMBER value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; int i = 0; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the MODELNO data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_MODELNO); while (reg != 0 && i < 15) { model[i] = reg; /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the MODELNO data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_MODELNO); i++; } } void selwd_read_serialno(bus_space_tag_t iot, bus_space_handle_t ioh, char* serial) { /* read the SERIAL NUMBER value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; int i = 0; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the SERIALNO data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_SERIALNO); while (reg != 0 && i < 15) { serial[i] = reg; /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the SERIALNO data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_SERIALNO); i++; } } void selwd_read_configid(bus_space_tag_t iot, bus_space_handle_t ioh, char* configid) { /* read the CONFIGURATION ID value from the controller */ /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); u_int8_t reg; int i = 0; /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the CONFIGID data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_CONFIGID); while (reg != 0 && i < 15) { configid[i] = reg; /* make sure status is 0 before proceeding */ selwd_abort(iot, ioh); /* write the READCFG command to the command register */ selwd_conf_enable(iot, ioh, SELWD_CMD_READCFG); /* read the CONFIGID data value from the data register */ reg = selwd_conf_read(iot, ioh, SELWD_CFG_CONFIGID); i++; } } On Tue, Apr 26, 2016 at 06:44:34AM -0600, Theo de Raadt wrote: > obviously you show the code, and then when the complexity/simplicity of it > is seen, some people can jump in and help. > > that is the traditional way: show it > > > We are embarking on a project where we will be using a number of > > industrially hardened computers manufactured by Schweitzer Engineering > > Laboratories, Inc. (SEL). SEL provides a very well whiten document > > describing certain special features of these computers. One of these is a > > hardware watchdog. > > > > We have contracted a systems integrator to write a device driver for this > > watchdog, with the stipulation that the code will be relapsed under the BSD > > licence. This work is essiantly complete, and I was wondering if someone > > could > > suggest to me the best way to submit this for (potential) inclusion in the > > OpenBSD base system? > > > > Thanks for all the good work on a really solid OS! > > > > > > -- > > A: Because it messes up the order in which people normally read text. > > Q: Why is top-posting such a bad thing? > > A: Top-posting. > > Q: What is the most annoying thing in e-mail? > > > -- A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? A: Top-posting. Q: What is the most annoying thing in e-mail? ----- End forwarded message ----- -- A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? A: Top-posting. Q: What is the most annoying thing in e-mail?