Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/ow/ow.c
- This file was added.
/*- | |||||
* Copyright (c) 2015 M. Warner Losh <imp@freebsd.org> | |||||
* All rights reserved. | |||||
* | |||||
* 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/malloc.h> | |||||
#include <sys/module.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 struct ow_timing timing_regular = { | |||||
.t_slot = 60, /* 60 to 120 */ | |||||
.t_low0 = 60, /* really 60 to 120 */ | |||||
.t_low1 = 1, /* really 1 to 15 */ | |||||
.t_release = 45, /* <= 45us */ | |||||
.t_rec = 1, /* at least 1us */ | |||||
.t_rdv = 15, /* 15us */ | |||||
.t_rstl = 480, /* 480us or more */ | |||||
.t_rsth = 480, /* 480us or more */ | |||||
.t_pdl = 60, /* 60us to 240us */ | |||||
.t_pdh = 60, /* 15us to 60us */ | |||||
.t_lowr = 1, /* 1us */ | |||||
}; | |||||
/* NB: Untested */ | |||||
static struct ow_timing timing_overdrive = { | |||||
.t_slot = 11, /* 6us to 16us */ | |||||
.t_low0 = 6, /* really 6 to 16 */ | |||||
.t_low1 = 1, /* really 1 to 2 */ | |||||
.t_release = 4, /* <= 4us */ | |||||
.t_rec = 1, /* at least 1us */ | |||||
.t_rdv = 2, /* 2us */ | |||||
.t_rstl = 48, /* 48us to 80us */ | |||||
.t_rsth = 48, /* 48us or more */ | |||||
.t_pdl = 8, /* 8us to 24us */ | |||||
.t_pdh = 2, /* 2us to 6us */ | |||||
.t_lowr = 1, /* 1us */ | |||||
}; | |||||
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; | |||||
device_t lldev; | |||||
struct ow_timing *t; | |||||
lldev = device_get_parent(ndev); | |||||
OWLL_RESET_AND_PRESENCE(lldev, &timing_regular, &present); | |||||
if (present == 1) { | |||||
printf("Reset said the device wasn't there.\n"); | |||||
return ENOENT; /* No devices acked the RESET */ | |||||
} | |||||
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; | |||||
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) { | |||||
again: | |||||
imp: Experience has shown that this loop is pathological when talking to a naked bus.
We get a… | |||||
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 the master (us) sends back a bit. If the | |||||
* device's bit doesn't match what we send back, that device | |||||
Done Inline ActionsExtra "the": wblock: Extra "the":
s/the where/where/ | |||||
* 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. | |||||
*/ | |||||
err = enumfp(dev, dev); | |||||
if (err != 0) { | |||||
device_printf(dev, "Could not enumerate: %d\n", err); | |||||
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; | |||||
} | |||||
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. | |||||
*/ | |||||
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; | |||||
} | |||||
/* | |||||
* Not sure this is really needed. I'm having trouble figuring out what | |||||
* location means in the context of the one wire bus. | |||||
*/ | |||||
static int | |||||
ow_child_location_str(device_t dev, device_t child, char *buf, | |||||
size_t buflen) | |||||
{ | |||||
snprintf(buf, buflen, ""); | |||||
return (0); | |||||
} | |||||
static int | |||||
ow_child_pnpinfo_str(device_t dev, device_t child, char *buf, | |||||
size_t buflen) | |||||
{ | |||||
struct ow_devinfo *di; | |||||
di = device_get_ivars(child); | |||||
snprintf(buf, buflen, "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_str, ow_child_pnpinfo_str), | |||||
DEVMETHOD(bus_child_location_str, ow_child_location_str), | |||||
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); |
Experience has shown that this loop is pathological when talking to a naked bus.
We get a count of all the possible address IDs, most of which have CRC errors.
At effectively SPLHIGH().
This must be mitigated somehow.
Some suggestions: