freebsd-dev/sys/dev/ow/ow.c
Warner Losh ddfc9c4c59 newbus: Move from bus_child_{pnpinfo,location}_src to bus_child_{pnpinfo,location} with sbuf
Now that the upper layers all go through a layer to tie into these
information functions that translates an sbuf into char * and len. The
current interface suffers issues of what to do in cases of truncation,
etc. Instead, migrate all these functions to using struct sbuf and these
issues go away. The caller is also in charge of any memory allocation
and/or expansion that's needed during this process.

Create a bus_generic_child_{pnpinfo,location} and make it default. It
just returns success. This is for those busses that have no information
for these items. Migrate the now-empty routines to using this as
appropriate.

Document these new interfaces with man pages, and oversight from before.

Reviewed by:		jhb, bcr
Sponsored by:		Netflix
Differential Revision:	https://reviews.freebsd.org/D29937
2021-06-22 20:52:06 -06:00

733 lines
19 KiB
C

/*-
* Copyright (c) 2015 M. Warner Losh <imp@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/errno.h>
#include <sys/libkern.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/sbuf.h>
#include <sys/sysctl.h>
#include <dev/ow/ow.h>
#include <dev/ow/owll.h>
#include <dev/ow/own.h>
/*
* lldev - link level device
* ndev - network / transport device (this module)
* pdev - presentation device (children of this module)
*/
typedef int ow_enum_fn(device_t, device_t);
typedef int ow_found_fn(device_t, romid_t);
struct ow_softc
{
device_t dev; /* Newbus driver back pointer */
struct mtx mtx; /* bus mutex */
device_t owner; /* bus owner, if != NULL */
};
struct ow_devinfo
{
romid_t romid;
};
static int ow_acquire_bus(device_t ndev, device_t pdev, int how);
static void ow_release_bus(device_t ndev, device_t pdev);
#define OW_LOCK(_sc) mtx_lock(&(_sc)->mtx)
#define OW_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
#define OW_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
#define OW_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
#define OW_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
static MALLOC_DEFINE(M_OW, "ow", "House keeping data for 1wire bus");
static const struct ow_timing timing_regular_min = {
.t_slot = 60,
.t_low0 = 60,
.t_low1 = 1,
.t_release = 0,
.t_rec = 1,
.t_rdv = 15, /* fixed */
.t_rstl = 480,
.t_rsth = 480,
.t_pdl = 60,
.t_pdh = 15,
.t_lowr = 1,
};
static const struct ow_timing timing_regular_max = {
.t_slot = 120,
.t_low0 = 120,
.t_low1 = 15,
.t_release = 45,
.t_rec = 960, /* infinity */
.t_rdv = 15, /* fixed */
.t_rstl = 960, /* infinity */
.t_rsth = 960, /* infinity */
.t_pdl = 240, /* 60us to 240us */
.t_pdh = 60, /* 15us to 60us */
.t_lowr = 15, /* 1us */
};
static struct ow_timing timing_regular = {
.t_slot = 60, /* 60 <= t < 120 */
.t_low0 = 60, /* 60 <= t < t_slot < 120 */
.t_low1 = 1, /* 1 <= t < 15 */
.t_release = 45, /* 0 <= t < 45 */
.t_rec = 15, /* 1 <= t < inf */
.t_rdv = 15, /* t == 15 */
.t_rstl = 480, /* 480 <= t < inf */
.t_rsth = 480, /* 480 <= t < inf */
.t_pdl = 60, /* 60 <= t < 240 */
.t_pdh = 60, /* 15 <= t < 60 */
.t_lowr = 1, /* 1 <= t < 15 */
};
/* NB: Untested */
static const struct ow_timing timing_overdrive_min = {
.t_slot = 6,
.t_low0 = 6,
.t_low1 = 1,
.t_release = 0,
.t_rec = 1,
.t_rdv = 2, /* fixed */
.t_rstl = 48,
.t_rsth = 48,
.t_pdl = 8,
.t_pdh = 2,
.t_lowr = 1,
};
static const struct ow_timing timing_overdrive_max = {
.t_slot = 16,
.t_low0 = 16,
.t_low1 = 2,
.t_release = 4,
.t_rec = 960, /* infinity */
.t_rdv = 2, /* fixed */
.t_rstl = 80,
.t_rsth = 960, /* infinity */
.t_pdl = 24,
.t_pdh = 6,
.t_lowr = 2,
};
static struct ow_timing timing_overdrive = {
.t_slot = 11, /* 6 <= t < 16 */
.t_low0 = 6, /* 6 <= t < t_slot < 16 */
.t_low1 = 1, /* 1 <= t < 2 */
.t_release = 4, /* 0 <= t < 4 */
.t_rec = 1, /* 1 <= t < inf */
.t_rdv = 2, /* t == 2 */
.t_rstl = 48, /* 48 <= t < 80 */
.t_rsth = 48, /* 48 <= t < inf */
.t_pdl = 8, /* 8 <= t < 24 */
.t_pdh = 2, /* 2 <= t < 6 */
.t_lowr = 1, /* 1 <= t < 2 */
};
SYSCTL_NODE(_hw, OID_AUTO, ow, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"1-Wire protocol");
SYSCTL_NODE(_hw_ow, OID_AUTO, regular, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Regular mode timings");
SYSCTL_NODE(_hw_ow, OID_AUTO, overdrive, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
"Overdrive mode timings");
#define _OW_TIMING_SYSCTL(mode, param) \
static int \
sysctl_ow_timing_ ## mode ## _ ## param(SYSCTL_HANDLER_ARGS) \
{ \
int val = timing_ ## mode.param; \
int err; \
err = sysctl_handle_int(oidp, &val, 0, req); \
if (err != 0 || req->newptr == NULL) \
return (err); \
if (val < timing_ ## mode ## _min.param) \
return (EINVAL); \
else if (val >= timing_ ## mode ## _max.param) \
return (EINVAL); \
timing_ ## mode.param = val; \
return (0); \
} \
SYSCTL_PROC(_hw_ow_ ## mode, OID_AUTO, param, \
CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), \
sysctl_ow_timing_ ## mode ## _ ## param, "I", \
"1-Wire timing parameter in microseconds (-1 resets to default)")
#define OW_TIMING_SYSCTL(param) \
_OW_TIMING_SYSCTL(regular, param); \
_OW_TIMING_SYSCTL(overdrive, param)
OW_TIMING_SYSCTL(t_slot);
OW_TIMING_SYSCTL(t_low0);
OW_TIMING_SYSCTL(t_low1);
OW_TIMING_SYSCTL(t_release);
OW_TIMING_SYSCTL(t_rec);
OW_TIMING_SYSCTL(t_rdv);
OW_TIMING_SYSCTL(t_rstl);
OW_TIMING_SYSCTL(t_rsth);
OW_TIMING_SYSCTL(t_pdl);
OW_TIMING_SYSCTL(t_pdh);
OW_TIMING_SYSCTL(t_lowr);
#undef _OW_TIMING_SYSCTL
#undef OW_TIMING_SYSCTL
static void
ow_send_byte(device_t lldev, struct ow_timing *t, uint8_t byte)
{
int i;
for (i = 0; i < 8; i++)
if (byte & (1 << i))
OWLL_WRITE_ONE(lldev, t);
else
OWLL_WRITE_ZERO(lldev, t);
}
static void
ow_read_byte(device_t lldev, struct ow_timing *t, uint8_t *bytep)
{
int i;
uint8_t byte = 0;
int bit;
for (i = 0; i < 8; i++) {
OWLL_READ_DATA(lldev, t, &bit);
byte |= bit << i;
}
*bytep = byte;
}
static int
ow_send_command(device_t ndev, device_t pdev, struct ow_cmd *cmd)
{
int present, i, bit, tries;
device_t lldev;
struct ow_timing *t;
lldev = device_get_parent(ndev);
/*
* Retry the reset a couple of times before giving up.
*/
tries = 4;
do {
OWLL_RESET_AND_PRESENCE(lldev, &timing_regular, &present);
if (present == 1)
device_printf(ndev, "Reset said no device on bus?.\n");
} while (present == 1 && tries-- > 0);
if (present == 1) {
device_printf(ndev, "Reset said the device wasn't there.\n");
return ENOENT; /* No devices acked the RESET */
}
if (present == -1) {
device_printf(ndev, "Reset discovered bus wired wrong.\n");
return ENOENT;
}
for (i = 0; i < cmd->rom_len; i++)
ow_send_byte(lldev, &timing_regular, cmd->rom_cmd[i]);
for (i = 0; i < cmd->rom_read_len; i++)
ow_read_byte(lldev, &timing_regular, cmd->rom_read + i);
if (cmd->xpt_len) {
/*
* Per AN937, the reset pulse and ROM level are always
* done with the regular timings. Certain ROM commands
* put the device into overdrive mode for the remainder
* of the data transfer, which is why we have to pass the
* timings here. Commands that need to be handled like this
* are expected to be flagged by the client.
*/
t = (cmd->flags & OW_FLAG_OVERDRIVE) ?
&timing_overdrive : &timing_regular;
for (i = 0; i < cmd->xpt_len; i++)
ow_send_byte(lldev, t, cmd->xpt_cmd[i]);
if (cmd->flags & OW_FLAG_READ_BIT) {
memset(cmd->xpt_read, 0, (cmd->xpt_read_len + 7) / 8);
for (i = 0; i < cmd->xpt_read_len; i++) {
OWLL_READ_DATA(lldev, t, &bit);
cmd->xpt_read[i / 8] |= bit << (i % 8);
}
} else {
for (i = 0; i < cmd->xpt_read_len; i++)
ow_read_byte(lldev, t, cmd->xpt_read + i);
}
}
return 0;
}
static int
ow_search_rom(device_t lldev, device_t dev)
{
struct ow_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.rom_cmd[0] = SEARCH_ROM;
cmd.rom_len = 1;
return ow_send_command(lldev, dev, &cmd);
}
#if 0
static int
ow_alarm_search(device_t lldev, device_t dev)
{
struct ow_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.rom_cmd[0] = ALARM_SEARCH;
cmd.rom_len = 1;
return ow_send_command(lldev, dev, &cmd);
}
#endif
static int
ow_add_child(device_t dev, romid_t romid)
{
struct ow_devinfo *di;
device_t child;
di = malloc(sizeof(*di), M_OW, M_WAITOK);
di->romid = romid;
child = device_add_child(dev, NULL, -1);
if (child == NULL) {
free(di, M_OW);
return ENOMEM;
}
device_set_ivars(child, di);
return (0);
}
static device_t
ow_child_by_romid(device_t dev, romid_t romid)
{
device_t *children, retval, child;
int nkid, i;
struct ow_devinfo *di;
if (device_get_children(dev, &children, &nkid) != 0)
return (NULL);
retval = NULL;
for (i = 0; i < nkid; i++) {
child = children[i];
di = device_get_ivars(child);
if (di->romid == romid) {
retval = child;
break;
}
}
free(children, M_TEMP);
return (retval);
}
/*
* CRC generator table -- taken from AN937 DOW CRC LOOKUP FUNCTION Table 2
*/
const uint8_t ow_crc_table[] = {
0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
219, 133,103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
140,210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113,147, 205,
17, 79, 173, 243, 112, 46, 204, 146, 211,141, 111, 49, 178, 236, 14, 80,
175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82,176, 238,
50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};
/*
* Converted from DO_CRC page 131 ANN937
*/
static uint8_t
ow_crc(device_t ndev, device_t pdev, uint8_t *buffer, size_t len)
{
uint8_t crc = 0;
int i;
for (i = 0; i < len; i++)
crc = ow_crc_table[crc ^ buffer[i]];
return crc;
}
static int
ow_check_crc(romid_t romid)
{
return ow_crc(NULL, NULL, (uint8_t *)&romid, sizeof(romid)) == 0;
}
static int
ow_device_found(device_t dev, romid_t romid)
{
/* XXX Move this up into enumerate? */
/*
* All valid ROM IDs have a valid CRC. Check that first.
*/
if (!ow_check_crc(romid)) {
device_printf(dev, "Device romid %8D failed CRC.\n",
&romid, ":");
return EINVAL;
}
/*
* If we've seen this child before, don't add a new one for it.
*/
if (ow_child_by_romid(dev, romid) != NULL)
return 0;
return ow_add_child(dev, romid);
}
static int
ow_enumerate(device_t dev, ow_enum_fn *enumfp, ow_found_fn *foundfp)
{
device_t lldev = device_get_parent(dev);
int first, second, i, dir, prior, last, err, retries;
uint64_t probed, last_mask;
int sanity = 10;
prior = -1;
last_mask = 0;
retries = 0;
last = -2;
err = ow_acquire_bus(dev, dev, OWN_DONTWAIT);
if (err != 0)
return err;
while (last != -1) {
if (sanity-- < 0) {
printf("Reached the sanity limit\n");
return EIO;
}
again:
probed = 0;
last = -1;
/*
* See AN397 section 5.II.C.3 for the algorithm (though a bit
* poorly stated). The search command forces each device to
* send ROM ID bits one at a time (first the bit, then the
* complement) the master (us) sends back a bit. If the
* device's bit doesn't match what we send back, that device
* stops sending bits back. So each time through we remember
* where we made the last decision (always 0). If there's a
* conflict there this time (and there will be in the absence
* of a hardware failure) we go with 1. This way, we prune the
* devices on the bus and wind up with a unique ROM. We know
* we're done when we detect no new conflicts. The same
* algorithm is used for devices in alarm state as well.
*
* In addition, experience has shown that sometimes devices
* stop responding in the middle of enumeration, so try this
* step again a few times when that happens. It is unclear if
* this is due to a nosiy electrical environment or some odd
* timing issue.
*/
/*
* The enumeration command should be successfully sent, if not,
* we have big issues on the bus so punt. Lower layers report
* any unusual errors, so we don't need to here.
*/
err = enumfp(dev, dev);
if (err != 0)
return (err);
for (i = 0; i < 64; i++) {
OWLL_READ_DATA(lldev, &timing_regular, &first);
OWLL_READ_DATA(lldev, &timing_regular, &second);
switch (first | second << 1) {
case 0: /* Conflict */
if (i < prior)
dir = (last_mask >> i) & 1;
else
dir = i == prior;
if (dir == 0)
last = i;
break;
case 1: /* 1 then 0 -> 1 for all */
dir = 1;
break;
case 2: /* 0 then 1 -> 0 for all */
dir = 0;
break;
case 3:
/*
* No device responded. This is unexpected, but
* experience has shown that on some platforms
* we miss a timing window, or otherwise have
* an issue. Start this step over. Since we've
* not updated prior yet, we can just jump to
* the top of the loop for a re-do of this step.
*/
printf("oops, starting over\n");
if (++retries > 5)
return (EIO);
goto again;
default: /* NOTREACHED */
__assert_unreachable();
}
if (dir) {
OWLL_WRITE_ONE(lldev, &timing_regular);
probed |= 1ull << i;
} else {
OWLL_WRITE_ZERO(lldev, &timing_regular);
}
}
retries = 0;
foundfp(dev, probed);
last_mask = probed;
prior = last;
}
ow_release_bus(dev, dev);
return (0);
}
static int
ow_probe(device_t dev)
{
device_set_desc(dev, "1 Wire Bus");
return (BUS_PROBE_GENERIC);
}
static int
ow_attach(device_t ndev)
{
struct ow_softc *sc;
/*
* Find all the devices on the bus. We don't probe / attach them in the
* enumeration phase. We do this because we want to allow the probe /
* attach routines of the child drivers to have as full an access to the
* bus as possible. While we reset things before the next step of the
* search (so it would likely be OK to allow access by the clients to
* the bus), it is more conservative to find them all, then to do the
* attach of the devices. This also allows the child devices to have
* more knowledge of the bus. We also ignore errors from the enumeration
* because they might happen after we've found a few devices.
*/
sc = device_get_softc(ndev);
sc->dev = ndev;
mtx_init(&sc->mtx, device_get_nameunit(sc->dev), "ow", MTX_DEF);
ow_enumerate(ndev, ow_search_rom, ow_device_found);
return bus_generic_attach(ndev);
}
static int
ow_detach(device_t ndev)
{
device_t *children, child;
int nkid, i;
struct ow_devinfo *di;
struct ow_softc *sc;
sc = device_get_softc(ndev);
/*
* detach all the children first. This is blocking until any threads
* have stopped, etc.
*/
bus_generic_detach(ndev);
/*
* We delete all the children, and free up the ivars
*/
if (device_get_children(ndev, &children, &nkid) != 0)
return ENOMEM;
for (i = 0; i < nkid; i++) {
child = children[i];
di = device_get_ivars(child);
free(di, M_OW);
device_delete_child(ndev, child);
}
free(children, M_TEMP);
OW_LOCK_DESTROY(sc);
return 0;
}
static int
ow_child_pnpinfo(device_t dev, device_t child, struct sbuf *sb)
{
struct ow_devinfo *di;
di = device_get_ivars(child);
sbuf_printf(sb, "romid=%8D", &di->romid, ":");
return (0);
}
static int
ow_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
{
struct ow_devinfo *di;
romid_t **ptr;
di = device_get_ivars(child);
switch (which) {
case OW_IVAR_FAMILY:
*result = di->romid & 0xff;
break;
case OW_IVAR_ROMID:
ptr = (romid_t **)result;
*ptr = &di->romid;
break;
default:
return EINVAL;
}
return 0;
}
static int
ow_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
{
return EINVAL;
}
static int
ow_print_child(device_t ndev, device_t pdev)
{
int retval = 0;
struct ow_devinfo *di;
di = device_get_ivars(pdev);
retval += bus_print_child_header(ndev, pdev);
retval += printf(" romid %8D", &di->romid, ":");
retval += bus_print_child_footer(ndev, pdev);
return retval;
}
static void
ow_probe_nomatch(device_t ndev, device_t pdev)
{
struct ow_devinfo *di;
di = device_get_ivars(pdev);
device_printf(ndev, "romid %8D: no driver\n", &di->romid, ":");
}
static int
ow_acquire_bus(device_t ndev, device_t pdev, int how)
{
struct ow_softc *sc;
sc = device_get_softc(ndev);
OW_ASSERT_UNLOCKED(sc);
OW_LOCK(sc);
if (sc->owner != NULL) {
if (sc->owner == pdev)
panic("%s: %s recursively acquiring the bus.\n",
device_get_nameunit(ndev),
device_get_nameunit(pdev));
if (how == OWN_DONTWAIT) {
OW_UNLOCK(sc);
return EWOULDBLOCK;
}
while (sc->owner != NULL)
mtx_sleep(sc, &sc->mtx, 0, "owbuswait", 0);
}
sc->owner = pdev;
OW_UNLOCK(sc);
return 0;
}
static void
ow_release_bus(device_t ndev, device_t pdev)
{
struct ow_softc *sc;
sc = device_get_softc(ndev);
OW_ASSERT_UNLOCKED(sc);
OW_LOCK(sc);
if (sc->owner == NULL)
panic("%s: %s releasing unowned bus.", device_get_nameunit(ndev),
device_get_nameunit(pdev));
if (sc->owner != pdev)
panic("%s: %s don't own the bus. %s does. game over.",
device_get_nameunit(ndev), device_get_nameunit(pdev),
device_get_nameunit(sc->owner));
sc->owner = NULL;
wakeup(sc);
OW_UNLOCK(sc);
}
devclass_t ow_devclass;
static device_method_t ow_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, ow_probe),
DEVMETHOD(device_attach, ow_attach),
DEVMETHOD(device_detach, ow_detach),
/* Bus interface */
DEVMETHOD(bus_child_pnpinfo, ow_child_pnpinfo),
DEVMETHOD(bus_read_ivar, ow_read_ivar),
DEVMETHOD(bus_write_ivar, ow_write_ivar),
DEVMETHOD(bus_print_child, ow_print_child),
DEVMETHOD(bus_probe_nomatch, ow_probe_nomatch),
/* One Wire Network/Transport layer interface */
DEVMETHOD(own_send_command, ow_send_command),
DEVMETHOD(own_acquire_bus, ow_acquire_bus),
DEVMETHOD(own_release_bus, ow_release_bus),
DEVMETHOD(own_crc, ow_crc),
{ 0, 0 }
};
static driver_t ow_driver = {
"ow",
ow_methods,
sizeof(struct ow_softc),
};
DRIVER_MODULE(ow, owc, ow_driver, ow_devclass, 0, 0);
MODULE_VERSION(ow, 1);