freebsd-skq/sys/dev/arl/if_arl_isa.c
imp 4e70215e6b Make sure that we call if_free(ifp) after bus_teardown_intr. Since we
could get an interrupt after we free the ifp, and the interrupt
handler depended on the ifp being still alive, this could, in theory,
cause a crash.  Eliminate this possibility by moving the if_free to
after the bus_teardown_intr() call.
2005-09-19 03:10:21 +00:00

360 lines
8.5 KiB
C

/*-
* Copyright (c) 1999-2001, Ivan Sharov, Vitaly Belekhov.
* Copyright (c) 2004 Stanislav Svirid.
* 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, 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 AUTHOR AND CONTRIBUTORS ``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 AUTHOR OR CONTRIBUTORS 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.
*
* $RISS: if_arl/dev/arl/if_arl_isa.c,v 1.7 2004/03/16 05:30:38 count Exp $
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_inet.h"
#ifdef INET
#define ARLCACHE
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/socket.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_mib.h>
#include <net/if_media.h>
#include <isa/isavar.h>
#include <isa/pnpvar.h>
#include <isa/isa_common.h>
#include <machine/clock.h>
#include <machine/md_var.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_param.h>
#include <dev/arl/if_arlreg.h>
static void arl_isa_identify(driver_t *, device_t);
static int arl_isa_probe (device_t);
static int arl_isa_attach (device_t);
static int arl_isa_detach (device_t);
static char* arl_make_desc (u_int8_t, u_int8_t);
#define ARL_MAX_ATYPE_LEN 10
static struct arl_type {
u_int8_t type;
char* desc;
}
arl_type_list[] = {
{ 0, "450" },
{ 1, "650" },
{ 0xb, "670" },
{ 0xc, "670E" },
{ 0xd, "650E" },
{ 0xe, "440LT" },
{ 0x2e, "655" },
{ 0x6b, "IC2200" },
{ 0, 0 }
};
#define ARL_MAX_RTYPE_LEN 10
struct radio_type {
u_int8_t type;
char* desc;
} radio_type_list [] = {
{ 1, "092/094" },
{ 2, "020" },
{ 3, "092A" },
{ 4, "020B" },
{ 5, "095" },
{ 6, "024" },
{ 7, "025B" },
{ 8, "024B" },
{ 9, "024C" },
{10, "025C" },
{11, "024-1A" },
{12, "025-1A" },
};
static char*
arl_make_desc(hw_type, radio_mod)
u_int8_t hw_type;
u_int8_t radio_mod;
{
static char desc[80];
char atype[ARL_MAX_ATYPE_LEN], rtype[ARL_MAX_RTYPE_LEN];
int i;
*atype = *rtype = 0;
/* arl type */
for(i = 0; arl_type_list[i].desc; i++) {
if (arl_type_list[i].type == hw_type)
break;
}
if (arl_type_list[i].desc)
strncpy(atype, arl_type_list[i].desc, ARL_MAX_ATYPE_LEN);
else
snprintf(atype, ARL_MAX_ATYPE_LEN, "(0x%x)", hw_type);
/* radio type */
for(i = 0; radio_type_list[i].desc; i++)
if (radio_type_list[i].type == radio_mod)
break;
if (radio_type_list[i].desc)
strncpy(rtype, radio_type_list[i].desc, ARL_MAX_RTYPE_LEN);
else
snprintf(rtype, ARL_MAX_RTYPE_LEN, "(0x%x)", radio_mod);
snprintf(desc, 80, "ArLan type %s, radio module %s", atype, rtype);
return desc;
}
#define ARL_ADDR2VEC(addr) (1 << ((addr - ARL_BASE_START) / ARL_BASE_STEP))
static void
arl_isa_identify (driver_t *driver, device_t parent)
{
device_t child;
struct arl_softc *sc;
int chunk, found, i;
u_int16_t free_mem = 0xFFFF;
if (bootverbose)
printf("arl: in identify\n");
/* Try avoid already added devices */
for (i = 0; (child = device_find_child(parent, "arl", i)) != NULL; i++) {
chunk = bus_get_resource_start(child, SYS_RES_MEMORY, 0);
if (bootverbose)
device_printf(child, "found at iomem = 0x%0x\n", chunk);
if (chunk >= ARL_BASE_START && chunk <= ARL_BASE_END)
free_mem ^= ARL_ADDR2VEC(chunk);
}
if (bootverbose)
printf("arl: free mem vector = 0x%x\n", free_mem);
for (chunk = ARL_BASE_START; chunk <= ARL_BASE_END; chunk += ARL_BASE_STEP) {
/* If device 'arl' with this chunk was found early - skip it */
if ( !(free_mem & ARL_ADDR2VEC(chunk)) )
continue;
found = 0;
child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "arl", -1);
device_set_driver(child, driver);
sc = device_get_softc(child);
bzero(sc, sizeof(*sc));
bus_set_resource(child, SYS_RES_MEMORY, sc->mem_rid, chunk,
ARL_BASE_STEP);
if (arl_alloc_memory(child, sc->mem_rid, ARL_BASE_STEP) == 0) {
ar = (struct arl_private *) rman_get_virtual(sc->mem_res);
if (!bcmp(ar->textRegion, ARLAN_SIGN, sizeof(ARLAN_SIGN) - 1))
found++;
}
if (bootverbose)
device_printf(child, "%sfound at 0x%x\n",
!found ? "not " : "", chunk);
arl_release_resources(child);
if (!found) {
bus_delete_resource(child, SYS_RES_MEMORY, sc->mem_rid);
device_delete_child(parent, child);
}
}
}
static int
arl_isa_probe (device_t dev)
{
struct arl_softc *sc = device_get_softc(dev);
int error;
u_char *ptr;
u_int8_t irq;
if (isa_get_vendorid(dev))
return (ENXIO);
if (bootverbose)
device_printf(dev, "in probe\n");
bzero(sc, sizeof(struct arl_softc));
sc->arl_unit = device_get_unit(dev);
error = arl_alloc_memory(dev, 0, ARL_BASE_STEP);
if (error) {
if (bootverbose)
device_printf(dev, "Error allocating memory (%d)\n", error);
return (error);
}
ar = (struct arl_private *) rman_get_virtual(sc->mem_res);
if (bcmp(ar->textRegion, ARLAN_SIGN, sizeof(ARLAN_SIGN) - 1)) {
if (bootverbose)
device_printf(dev, "not found\n");
error = ENOENT;
goto bad;
}
irq = ar->irqLevel;
if (irq == 2)
irq = 9;
error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
if (error)
goto bad;
error = arl_alloc_irq(dev, 0, 0);
if (error) {
if (bootverbose)
device_printf(dev, "Can't allocate IRQ %d\n", irq);
goto bad;
}
ar->controlRegister = 1; /* freeze board */
/* Memory test */
for (ptr = (u_char *) ar;
ptr < ((u_char *) ar + ARL_BASE_STEP - 1); ptr++) {
u_char c;
c = *ptr; *ptr = ~(*ptr);
if (*ptr != (u_char)~c) {
device_printf(dev, "board memory failed at [%lx]\n",
rman_get_start(sc->mem_res) + (ptr - (u_char *)ar));
break; /* skip memory test */
}
}
bzero((void *) ar, ARL_BASE_STEP - 1); /* clear board ram */
if (arl_wait_reset(sc, 100, ARDELAY)) {
error = ENXIO;
goto bad;
}
if (ar->diagnosticInfo == 0xFF) {
device_set_desc_copy(dev, arl_make_desc(ar->hardwareType,
ar->radioModule));
error = 0;
} else {
if (bootverbose)
device_printf(dev, "board self-test failed (0x%x)!\n",
ar->diagnosticInfo);
error = ENXIO;
}
bad:
arl_release_resources(dev);
return (error);
}
static int
arl_isa_attach (device_t dev)
{
struct arl_softc *sc = device_get_softc(dev);
int error;
if (bootverbose)
device_printf(dev, "in attach\n");
arl_alloc_memory(dev, sc->mem_rid, ARL_BASE_STEP);
arl_alloc_irq(dev, sc->irq_rid, 0);
error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
arl_intr, sc, &sc->irq_handle);
if (error) {
arl_release_resources(dev);
return (error);
}
#if __FreeBSD_version < 502108
device_printf(dev, "Ethernet address %6D\n", IFP2ENADDR(sc->arl_ifp), ":");
#endif
return arl_attach(dev);
}
static int
arl_isa_detach(device_t dev)
{
struct arl_softc *sc = device_get_softc(dev);
arl_stop(sc);
ifmedia_removeall(&sc->arl_ifmedia);
bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
#if __FreeBSD_version < 500100
ether_ifdetach(sc->arl_ifp, ETHER_BPF_SUPPORTED);
#else
ether_ifdetach(sc->arl_ifp);
if_free(sc->arl_ifp);
#endif
arl_release_resources(dev);
return (0);
}
static device_method_t arl_isa_methods[] = {
/* Device interface */
DEVMETHOD(device_identify, arl_isa_identify),
DEVMETHOD(device_probe, arl_isa_probe),
DEVMETHOD(device_attach, arl_isa_attach),
DEVMETHOD(device_detach, arl_isa_detach),
{ 0, 0 }
};
static driver_t arl_isa_driver = {
"arl",
arl_isa_methods,
sizeof(struct arl_softc)
};
extern devclass_t arl_devclass;
DRIVER_MODULE(arl, isa, arl_isa_driver, arl_devclass, 0, 0);
MODULE_DEPEND(arl, isa, 1, 1, 1);
MODULE_DEPEND(arl, ether, 1, 1, 1);