2009-09-15 23:20:29 +04:00
|
|
|
/* $NetBSD: elinkxl.c,v 1.109 2009/09/15 19:20:30 dyoung Exp $ */
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
/*-
|
|
|
|
* Copyright (c) 1998 The NetBSD Foundation, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
|
|
* by Frank van der Linden.
|
|
|
|
*
|
|
|
|
* 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 THE NETBSD FOUNDATION, INC. 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 THE FOUNDATION 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.
|
|
|
|
*/
|
|
|
|
|
2001-11-13 16:14:31 +03:00
|
|
|
#include <sys/cdefs.h>
|
2009-09-15 23:20:29 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: elinkxl.c,v 1.109 2009/09/15 19:20:30 dyoung Exp $");
|
2001-11-13 16:14:31 +03:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
#include "bpfilter.h"
|
|
|
|
#include "rnd.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2000-03-23 10:01:25 +03:00
|
|
|
#include <sys/callout.h>
|
1998-11-04 03:29:28 +03:00
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#if NRND > 0
|
|
|
|
#include <sys/rnd.h>
|
|
|
|
#endif
|
|
|
|
|
2000-11-14 21:21:00 +03:00
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_ether.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
#include <net/bpf.h>
|
|
|
|
#include <net/bpfdesc.h>
|
|
|
|
#endif
|
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/cpu.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/intr.h>
|
1999-12-12 05:53:56 +03:00
|
|
|
#include <machine/endian.h>
|
1999-04-27 03:28:31 +04:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
#include <dev/mii/miivar.h>
|
|
|
|
#include <dev/mii/mii.h>
|
1999-11-17 20:56:52 +03:00
|
|
|
#include <dev/mii/mii_bitbang.h>
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
#include <dev/ic/elink3reg.h>
|
|
|
|
/* #include <dev/ic/elink3var.h> */
|
|
|
|
#include <dev/ic/elinkxlreg.h>
|
|
|
|
#include <dev/ic/elinkxlvar.h>
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
int exdebug = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* ifmedia callbacks */
|
2005-02-04 05:10:35 +03:00
|
|
|
int ex_media_chg(struct ifnet *ifp);
|
|
|
|
void ex_media_stat(struct ifnet *ifp, struct ifmediareq *req);
|
|
|
|
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
static int ex_ifflags_cb(struct ethercom *);
|
|
|
|
|
2005-02-04 05:10:35 +03:00
|
|
|
void ex_probe_media(struct ex_softc *);
|
|
|
|
void ex_set_filter(struct ex_softc *);
|
|
|
|
void ex_set_media(struct ex_softc *);
|
2008-04-14 14:54:21 +04:00
|
|
|
void ex_set_xcvr(struct ex_softc *, uint16_t);
|
2005-02-04 05:10:35 +03:00
|
|
|
struct mbuf *ex_get(struct ex_softc *, int);
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t ex_read_eeprom(struct ex_softc *, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
int ex_init(struct ifnet *);
|
|
|
|
void ex_read(struct ex_softc *);
|
|
|
|
void ex_reset(struct ex_softc *);
|
|
|
|
void ex_set_mc(struct ex_softc *);
|
|
|
|
void ex_getstats(struct ex_softc *);
|
|
|
|
void ex_printstats(struct ex_softc *);
|
|
|
|
void ex_tick(void *);
|
|
|
|
|
|
|
|
static int ex_eeprom_busy(struct ex_softc *);
|
|
|
|
static int ex_add_rxbuf(struct ex_softc *, struct ex_rxdesc *);
|
|
|
|
static void ex_init_txdescs(struct ex_softc *);
|
|
|
|
|
2006-11-05 10:59:21 +03:00
|
|
|
static void ex_setup_tx(struct ex_softc *);
|
2008-04-15 00:03:13 +04:00
|
|
|
static bool ex_shutdown(device_t, int);
|
2005-02-04 05:10:35 +03:00
|
|
|
static void ex_start(struct ifnet *);
|
|
|
|
static void ex_txstat(struct ex_softc *);
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
int ex_mii_readreg(device_t, int, int);
|
|
|
|
void ex_mii_writereg(device_t, int, int, int);
|
|
|
|
void ex_mii_statchg(device_t);
|
2005-02-04 05:10:35 +03:00
|
|
|
|
|
|
|
void ex_probemedia(struct ex_softc *);
|
1998-11-29 04:40:46 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Structure to map media-present bits in boards to ifmedia codes and
|
|
|
|
* printable media names. Used for table-driven ifmedia initialization.
|
|
|
|
*/
|
|
|
|
struct ex_media {
|
|
|
|
int exm_mpbit; /* media present bit */
|
|
|
|
const char *exm_name; /* name of medium */
|
|
|
|
int exm_ifmedia; /* ifmedia word for medium */
|
|
|
|
int exm_epmedia; /* ELINKMEDIA_* constant */
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Media table for 3c90x chips. Note that chips with MII have no
|
|
|
|
* `native' media.
|
|
|
|
*/
|
|
|
|
struct ex_media ex_native_media[] = {
|
|
|
|
{ ELINK_PCI_10BASE_T, "10baseT", IFM_ETHER|IFM_10_T,
|
|
|
|
ELINKMEDIA_10BASE_T },
|
|
|
|
{ ELINK_PCI_10BASE_T, "10baseT-FDX", IFM_ETHER|IFM_10_T|IFM_FDX,
|
|
|
|
ELINKMEDIA_10BASE_T },
|
|
|
|
{ ELINK_PCI_AUI, "10base5", IFM_ETHER|IFM_10_5,
|
|
|
|
ELINKMEDIA_AUI },
|
|
|
|
{ ELINK_PCI_BNC, "10base2", IFM_ETHER|IFM_10_2,
|
|
|
|
ELINKMEDIA_10BASE_2 },
|
|
|
|
{ ELINK_PCI_100BASE_TX, "100baseTX", IFM_ETHER|IFM_100_TX,
|
|
|
|
ELINKMEDIA_100BASE_TX },
|
|
|
|
{ ELINK_PCI_100BASE_TX, "100baseTX-FDX",IFM_ETHER|IFM_100_TX|IFM_FDX,
|
|
|
|
ELINKMEDIA_100BASE_TX },
|
|
|
|
{ ELINK_PCI_100BASE_FX, "100baseFX", IFM_ETHER|IFM_100_FX,
|
|
|
|
ELINKMEDIA_100BASE_FX },
|
|
|
|
{ ELINK_PCI_100BASE_MII,"manual", IFM_ETHER|IFM_MANUAL,
|
|
|
|
ELINKMEDIA_MII },
|
|
|
|
{ ELINK_PCI_100BASE_T4, "100baseT4", IFM_ETHER|IFM_100_T4,
|
|
|
|
ELINKMEDIA_100BASE_T4 },
|
|
|
|
{ 0, NULL, 0,
|
|
|
|
0 },
|
|
|
|
};
|
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
/*
|
|
|
|
* MII bit-bang glue.
|
|
|
|
*/
|
2008-04-15 00:03:13 +04:00
|
|
|
uint32_t ex_mii_bitbang_read(device_t);
|
|
|
|
void ex_mii_bitbang_write(device_t, uint32_t);
|
1999-11-17 20:56:52 +03:00
|
|
|
|
|
|
|
const struct mii_bitbang_ops ex_mii_bitbang_ops = {
|
|
|
|
ex_mii_bitbang_read,
|
|
|
|
ex_mii_bitbang_write,
|
|
|
|
{
|
|
|
|
ELINK_PHY_DATA, /* MII_BIT_MDO */
|
|
|
|
ELINK_PHY_DATA, /* MII_BIT_MDI */
|
|
|
|
ELINK_PHY_CLK, /* MII_BIT_MDC */
|
|
|
|
ELINK_PHY_DIR, /* MII_BIT_DIR_HOST_PHY */
|
|
|
|
0, /* MII_BIT_DIR_PHY_HOST */
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Back-end attach and configure.
|
|
|
|
*/
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_config(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t val;
|
|
|
|
uint8_t macaddr[ETHER_ADDR_LEN] = {0};
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2000-02-05 21:11:55 +03:00
|
|
|
int i, error, attach_stage;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2007-07-10 00:51:58 +04:00
|
|
|
callout_init(&sc->ex_mii_callout, 0);
|
2000-03-23 10:01:25 +03:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_reset(sc);
|
|
|
|
|
|
|
|
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR0);
|
|
|
|
macaddr[0] = val >> 8;
|
|
|
|
macaddr[1] = val & 0xff;
|
|
|
|
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR1);
|
|
|
|
macaddr[2] = val >> 8;
|
|
|
|
macaddr[3] = val & 0xff;
|
|
|
|
val = ex_read_eeprom(sc, EEPROM_OEM_ADDR2);
|
|
|
|
macaddr[4] = val >> 8;
|
|
|
|
macaddr[5] = val & 0xff;
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_normal_dev(sc->sc_dev, "MAC address %s\n", ether_sprintf(macaddr));
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-09-19 05:15:06 +04:00
|
|
|
if (sc->ex_conf & (EX_CONF_INV_LED_POLARITY|EX_CONF_PHY_POWER)) {
|
|
|
|
GO_WINDOW(2);
|
|
|
|
val = bus_space_read_2(iot, ioh, ELINK_W2_RESET_OPTIONS);
|
|
|
|
if (sc->ex_conf & EX_CONF_INV_LED_POLARITY)
|
|
|
|
val |= ELINK_RESET_OPT_LEDPOLAR;
|
|
|
|
if (sc->ex_conf & EX_CONF_PHY_POWER)
|
|
|
|
val |= ELINK_RESET_OPT_PHYPOWER;
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W2_RESET_OPTIONS, val);
|
1999-10-15 10:07:17 +04:00
|
|
|
}
|
2003-06-06 02:11:21 +04:00
|
|
|
if (sc->ex_conf & EX_CONF_NO_XCVR_PWR) {
|
|
|
|
GO_WINDOW(0);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W0_MFG_ID,
|
|
|
|
EX_XCVR_PWR_MAGICBITS);
|
|
|
|
}
|
1999-10-15 10:07:17 +04:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
attach_stage = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate the upload descriptors, and create and load the DMA
|
|
|
|
* map for them.
|
|
|
|
*/
|
|
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat,
|
2005-02-27 03:26:58 +03:00
|
|
|
EX_NUPD * sizeof (struct ex_upd), PAGE_SIZE, 0, &sc->sc_useg, 1,
|
2000-02-05 21:11:55 +03:00
|
|
|
&sc->sc_urseg, BUS_DMA_NOWAIT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't allocate upload descriptors, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 1;
|
|
|
|
|
2000-02-05 21:11:55 +03:00
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &sc->sc_useg, sc->sc_urseg,
|
2007-03-04 08:59:00 +03:00
|
|
|
EX_NUPD * sizeof (struct ex_upd), (void **)&sc->sc_upd,
|
1998-11-04 03:29:28 +03:00
|
|
|
BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't map upload descriptors, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 2;
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat,
|
|
|
|
EX_NUPD * sizeof (struct ex_upd), 1,
|
|
|
|
EX_NUPD * sizeof (struct ex_upd), 0, BUS_DMA_NOWAIT,
|
|
|
|
&sc->sc_upd_dmamap)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't create upload desc. DMA map, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 3;
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_upd_dmamap,
|
|
|
|
sc->sc_upd, EX_NUPD * sizeof (struct ex_upd), NULL,
|
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't load upload desc. DMA map, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 4;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate the download descriptors, and create and load the DMA
|
|
|
|
* map for them.
|
|
|
|
*/
|
|
|
|
if ((error = bus_dmamem_alloc(sc->sc_dmat,
|
2006-11-12 10:16:14 +03:00
|
|
|
DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN, PAGE_SIZE, 0, &sc->sc_dseg, 1,
|
2000-02-05 21:11:55 +03:00
|
|
|
&sc->sc_drseg, BUS_DMA_NOWAIT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't allocate download descriptors, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 5;
|
|
|
|
|
2000-02-05 21:11:55 +03:00
|
|
|
if ((error = bus_dmamem_map(sc->sc_dmat, &sc->sc_dseg, sc->sc_drseg,
|
2007-03-04 08:59:00 +03:00
|
|
|
DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN, (void **)&sc->sc_dpd,
|
1998-11-04 03:29:28 +03:00
|
|
|
BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't map download descriptors, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2006-11-12 10:16:14 +03:00
|
|
|
memset(sc->sc_dpd, 0, DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
attach_stage = 6;
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat,
|
2006-11-12 10:16:14 +03:00
|
|
|
DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN, 1,
|
|
|
|
DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN, 0, BUS_DMA_NOWAIT,
|
1998-11-04 03:29:28 +03:00
|
|
|
&sc->sc_dpd_dmamap)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't create download desc. DMA map, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 7;
|
|
|
|
|
|
|
|
if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dpd_dmamap,
|
2006-11-12 10:16:14 +03:00
|
|
|
sc->sc_dpd, DPDMEM_SIZE + EX_IP4CSUMTX_PADLEN, NULL,
|
1998-11-04 03:29:28 +03:00
|
|
|
BUS_DMA_NOWAIT)) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't load download desc. DMA map, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2006-11-12 10:16:14 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
|
|
|
DPDMEMPAD_OFF, EX_IP4CSUMTX_PADLEN, BUS_DMASYNC_PREWRITE);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
attach_stage = 8;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the transmit buffer DMA maps.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < EX_NDPD; i++) {
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
|
|
|
|
EX_NTFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT,
|
|
|
|
&sc->sc_tx_dmamaps[i])) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-04-08 16:07:25 +04:00
|
|
|
"can't create tx DMA map %d, error = %d\n",
|
|
|
|
i, error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 9;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the receive buffer DMA maps.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < EX_NUPD; i++) {
|
|
|
|
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
|
|
|
|
EX_NRFRAGS, MCLBYTES, 0, BUS_DMA_NOWAIT,
|
|
|
|
&sc->sc_rx_dmamaps[i])) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
2008-04-08 16:07:25 +04:00
|
|
|
"can't create rx DMA map %d, error = %d\n",
|
|
|
|
i, error);
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
attach_stage = 10;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create ring of upload descriptors, only once. The DMA engine
|
|
|
|
* will loop over this when receiving packets, stalling if it
|
|
|
|
* hits an UPD with a finished receive.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < EX_NUPD; i++) {
|
|
|
|
sc->sc_rxdescs[i].rx_dmamap = sc->sc_rx_dmamaps[i];
|
|
|
|
sc->sc_rxdescs[i].rx_upd = &sc->sc_upd[i];
|
1999-04-27 03:28:31 +04:00
|
|
|
sc->sc_upd[i].upd_frags[0].fr_len =
|
1999-12-12 05:53:56 +03:00
|
|
|
htole32((MCLBYTES - 2) | EX_FR_LAST);
|
1998-11-04 03:29:28 +03:00
|
|
|
if (ex_add_rxbuf(sc, &sc->sc_rxdescs[i]) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't allocate or map rx buffers\n");
|
1998-11-04 03:29:28 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_upd_dmamap, 0,
|
|
|
|
EX_NUPD * sizeof (struct ex_upd),
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
ex_init_txdescs(sc);
|
|
|
|
|
|
|
|
attach_stage = 11;
|
|
|
|
|
|
|
|
|
|
|
|
GO_WINDOW(3);
|
|
|
|
val = bus_space_read_2(iot, ioh, ELINK_W3_RESET_OPTIONS);
|
|
|
|
if (val & ELINK_MEDIACAP_MII)
|
|
|
|
sc->ex_conf |= EX_CONF_MII;
|
|
|
|
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
|
1998-11-29 04:40:46 +03:00
|
|
|
/*
|
|
|
|
* Initialize our media structures and MII info. We'll
|
|
|
|
* probe the MII if we discover that we have one.
|
|
|
|
*/
|
|
|
|
sc->ex_mii.mii_ifp = ifp;
|
|
|
|
sc->ex_mii.mii_readreg = ex_mii_readreg;
|
|
|
|
sc->ex_mii.mii_writereg = ex_mii_writereg;
|
|
|
|
sc->ex_mii.mii_statchg = ex_mii_statchg;
|
2002-10-22 04:01:55 +04:00
|
|
|
ifmedia_init(&sc->ex_mii.mii_media, IFM_IMASK, ex_media_chg,
|
1998-11-29 04:40:46 +03:00
|
|
|
ex_media_stat);
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
if (sc->ex_conf & EX_CONF_MII) {
|
|
|
|
/*
|
|
|
|
* Find PHY, extract media information from it.
|
1999-09-02 01:03:02 +04:00
|
|
|
* First, select the right transceiver.
|
1998-11-04 03:29:28 +03:00
|
|
|
*/
|
2003-05-10 00:54:18 +04:00
|
|
|
ex_set_xcvr(sc, val);
|
1999-09-02 01:03:02 +04:00
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
mii_attach(sc->sc_dev, &sc->ex_mii, 0xffffffff,
|
2000-02-02 20:09:38 +03:00
|
|
|
MII_PHY_ANY, MII_OFFSET_ANY, 0);
|
1998-11-04 03:29:28 +03:00
|
|
|
if (LIST_FIRST(&sc->ex_mii.mii_phys) == NULL) {
|
|
|
|
ifmedia_add(&sc->ex_mii.mii_media, IFM_ETHER|IFM_NONE,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_set(&sc->ex_mii.mii_media, IFM_ETHER|IFM_NONE);
|
|
|
|
} else {
|
|
|
|
ifmedia_set(&sc->ex_mii.mii_media, IFM_ETHER|IFM_AUTO);
|
|
|
|
}
|
1998-11-29 04:40:46 +03:00
|
|
|
} else
|
|
|
|
ex_probemedia(sc);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2008-04-15 01:20:41 +04:00
|
|
|
strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
|
1998-11-04 03:29:28 +03:00
|
|
|
ifp->if_softc = sc;
|
|
|
|
ifp->if_start = ex_start;
|
|
|
|
ifp->if_ioctl = ex_ioctl;
|
|
|
|
ifp->if_watchdog = ex_watchdog;
|
2000-10-11 20:57:45 +04:00
|
|
|
ifp->if_init = ex_init;
|
|
|
|
ifp->if_stop = ex_stop;
|
1998-11-04 03:29:28 +03:00
|
|
|
ifp->if_flags =
|
|
|
|
IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
|
2005-02-06 06:15:14 +03:00
|
|
|
sc->sc_if_flags = ifp->if_flags;
|
2000-12-14 09:27:23 +03:00
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-10-17 20:16:52 +04:00
|
|
|
/*
|
|
|
|
* We can support 802.1Q VLAN-sized frames.
|
|
|
|
*/
|
|
|
|
sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
|
|
|
|
|
2001-06-02 20:17:06 +04:00
|
|
|
/*
|
|
|
|
* The 3c90xB has hardware IPv4/TCPv4/UDPv4 checksum support.
|
|
|
|
*/
|
|
|
|
if (sc->ex_conf & EX_CONF_90XB)
|
2005-05-02 19:34:31 +04:00
|
|
|
sc->sc_ethercom.ec_if.if_capabilities |=
|
|
|
|
IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
|
|
|
|
IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
|
|
|
|
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx;
|
2001-06-02 20:17:06 +04:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
if_attach(ifp);
|
|
|
|
ether_ifattach(ifp, macaddr);
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
ether_set_ifflags_cb(&sc->sc_ethercom, ex_ifflags_cb);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(1);
|
|
|
|
|
|
|
|
sc->tx_start_thresh = 20;
|
|
|
|
sc->tx_succ_ok = 0;
|
|
|
|
|
|
|
|
/* TODO: set queues to 0 */
|
|
|
|
|
|
|
|
#if NRND > 0
|
2008-04-15 00:03:13 +04:00
|
|
|
rnd_attach_source(&sc->rnd_source, device_xname(sc->sc_dev),
|
1999-02-28 20:08:05 +03:00
|
|
|
RND_TYPE_NET, 0);
|
1998-11-04 03:29:28 +03:00
|
|
|
#endif
|
|
|
|
|
2009-09-05 18:19:30 +04:00
|
|
|
if (pmf_device_register1(sc->sc_dev, NULL, NULL, ex_shutdown))
|
2008-04-15 00:03:13 +04:00
|
|
|
pmf_class_network_register(sc->sc_dev, &sc->sc_ethercom.ec_if);
|
2009-09-05 18:19:30 +04:00
|
|
|
else
|
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"couldn't establish power handler\n");
|
2000-05-29 21:37:12 +04:00
|
|
|
|
|
|
|
/* The attach is successful. */
|
|
|
|
sc->ex_flags |= EX_FLAGS_ATTACHED;
|
1998-11-04 03:29:28 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
/*
|
|
|
|
* Free any resources we've allocated during the failed attach
|
|
|
|
* attempt. Do this in reverse order and fall though.
|
|
|
|
*/
|
|
|
|
switch (attach_stage) {
|
|
|
|
case 11:
|
|
|
|
{
|
|
|
|
struct ex_rxdesc *rxd;
|
|
|
|
|
|
|
|
for (i = 0; i < EX_NUPD; i++) {
|
|
|
|
rxd = &sc->sc_rxdescs[i];
|
|
|
|
if (rxd->rx_mbhead != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap);
|
|
|
|
m_freem(rxd->rx_mbhead);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 10:
|
|
|
|
for (i = 0; i < EX_NUPD; i++)
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmamaps[i]);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 9:
|
|
|
|
for (i = 0; i < EX_NDPD; i++)
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmamaps[i]);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 8:
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dpd_dmamap);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dpd_dmamap);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 6:
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_dpd,
|
1998-11-04 03:29:28 +03:00
|
|
|
EX_NDPD * sizeof (struct ex_dpd));
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 5:
|
2000-02-05 21:11:55 +03:00
|
|
|
bus_dmamem_free(sc->sc_dmat, &sc->sc_dseg, sc->sc_drseg);
|
1998-11-04 03:29:28 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_upd_dmamap);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_upd_dmamap);
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 2:
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_upd,
|
1998-11-04 03:29:28 +03:00
|
|
|
EX_NUPD * sizeof (struct ex_upd));
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
|
|
|
case 1:
|
2000-02-05 21:11:55 +03:00
|
|
|
bus_dmamem_free(sc->sc_dmat, &sc->sc_useg, sc->sc_urseg);
|
1998-11-04 03:29:28 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
1998-11-29 04:40:46 +03:00
|
|
|
/*
|
|
|
|
* Find the media present on non-MII chips.
|
|
|
|
*/
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_probemedia(struct ex_softc *sc)
|
1998-11-29 04:40:46 +03:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct ifmedia *ifm = &sc->ex_mii.mii_media;
|
|
|
|
struct ex_media *exm;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t config1, reset_options, default_media;
|
1998-11-29 04:40:46 +03:00
|
|
|
int defmedia = 0;
|
|
|
|
const char *sep = "", *defmedianame = NULL;
|
|
|
|
|
|
|
|
GO_WINDOW(3);
|
|
|
|
config1 = bus_space_read_2(iot, ioh, ELINK_W3_INTERNAL_CONFIG + 2);
|
|
|
|
reset_options = bus_space_read_1(iot, ioh, ELINK_W3_RESET_OPTIONS);
|
|
|
|
GO_WINDOW(0);
|
|
|
|
|
|
|
|
default_media = (config1 & CONFIG_MEDIAMASK) >> CONFIG_MEDIAMASK_SHIFT;
|
|
|
|
|
|
|
|
/* Sanity check that there are any media! */
|
|
|
|
if ((reset_options & ELINK_PCI_MEDIAMASK) == 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "no media present!\n");
|
1998-11-29 04:40:46 +03:00
|
|
|
ifmedia_add(ifm, IFM_ETHER|IFM_NONE, 0, NULL);
|
|
|
|
ifmedia_set(ifm, IFM_ETHER|IFM_NONE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_normal_dev(sc->sc_dev, "");
|
|
|
|
|
2003-01-31 03:26:25 +03:00
|
|
|
#define PRINT(str) aprint_normal("%s%s", sep, str); sep = ", "
|
1998-11-29 04:40:46 +03:00
|
|
|
|
|
|
|
for (exm = ex_native_media; exm->exm_name != NULL; exm++) {
|
|
|
|
if (reset_options & exm->exm_mpbit) {
|
|
|
|
/*
|
|
|
|
* Default media is a little complicated. We
|
|
|
|
* support full-duplex which uses the same
|
|
|
|
* reset options bit.
|
|
|
|
*
|
|
|
|
* XXX Check EEPROM for default to FDX?
|
|
|
|
*/
|
|
|
|
if (exm->exm_epmedia == default_media) {
|
|
|
|
if ((exm->exm_ifmedia & IFM_FDX) == 0) {
|
|
|
|
defmedia = exm->exm_ifmedia;
|
|
|
|
defmedianame = exm->exm_name;
|
|
|
|
}
|
|
|
|
} else if (defmedia == 0) {
|
|
|
|
defmedia = exm->exm_ifmedia;
|
|
|
|
defmedianame = exm->exm_name;
|
|
|
|
}
|
|
|
|
ifmedia_add(ifm, exm->exm_ifmedia, exm->exm_epmedia,
|
|
|
|
NULL);
|
|
|
|
PRINT(exm->exm_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef PRINT
|
|
|
|
|
|
|
|
#ifdef DIAGNOSTIC
|
|
|
|
if (defmedia == 0)
|
|
|
|
panic("ex_probemedia: impossible");
|
|
|
|
#endif
|
|
|
|
|
2003-01-31 03:26:25 +03:00
|
|
|
aprint_normal(", default %s\n", defmedianame);
|
1998-11-29 04:40:46 +03:00
|
|
|
ifmedia_set(ifm, defmedia);
|
|
|
|
}
|
|
|
|
|
2006-11-05 10:59:21 +03:00
|
|
|
/*
|
|
|
|
* Setup transmitter parameters.
|
|
|
|
*/
|
|
|
|
static void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_setup_tx(struct ex_softc *sc)
|
2006-11-05 10:59:21 +03:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable reclaim threshold for 90xB, set free threshold to
|
|
|
|
* 6 * 256 = 1536 for 90x.
|
|
|
|
*/
|
|
|
|
if (sc->ex_conf & EX_CONF_90XB)
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
ELINK_TXRECLTHRESH | 255);
|
|
|
|
else
|
|
|
|
bus_space_write_1(iot, ioh, ELINK_TXFREETHRESH, 6);
|
|
|
|
|
|
|
|
/* Setup early transmission start threshold. */
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
ELINK_TXSTARTTHRESH | sc->tx_start_thresh);
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Bring device up.
|
|
|
|
*/
|
2000-10-11 20:57:45 +04:00
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_init(struct ifnet *ifp)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2000-10-11 20:57:45 +04:00
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2001-01-30 22:27:39 +03:00
|
|
|
int i;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t val;
|
2001-01-30 22:27:39 +03:00
|
|
|
int error = 0;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2001-01-30 22:27:39 +03:00
|
|
|
if ((error = ex_enable(sc)) != 0)
|
|
|
|
goto out;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
ex_waitcmd(sc);
|
2000-10-11 20:57:45 +04:00
|
|
|
ex_stop(ifp, 0);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2006-10-29 08:56:35 +03:00
|
|
|
GO_WINDOW(2);
|
|
|
|
|
|
|
|
/* Turn on PHY power. */
|
|
|
|
if (sc->ex_conf & (EX_CONF_PHY_POWER | EX_CONF_INV_LED_POLARITY)) {
|
|
|
|
val = bus_space_read_2(iot, ioh, ELINK_W2_RESET_OPTIONS);
|
|
|
|
if (sc->ex_conf & EX_CONF_PHY_POWER)
|
|
|
|
val |= ELINK_RESET_OPT_PHYPOWER; /* turn on PHY power */
|
|
|
|
if (sc->ex_conf & EX_CONF_INV_LED_POLARITY)
|
|
|
|
val |= ELINK_RESET_OPT_LEDPOLAR; /* invert LED polarity */
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W2_RESET_OPTIONS, val);
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Set the station address and clear the station mask. The latter
|
|
|
|
* is needed for 90x cards, 0 is the default for 90xB cards.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
|
|
bus_space_write_1(iot, ioh, ELINK_W2_ADDR_0 + i,
|
2007-08-27 02:45:55 +04:00
|
|
|
CLLADDR(ifp->if_sadl)[i]);
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_write_1(iot, ioh, ELINK_W2_RECVMASK_0 + i, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
GO_WINDOW(3);
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, RX_RESET);
|
|
|
|
ex_waitcmd(sc);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_RESET);
|
|
|
|
ex_waitcmd(sc);
|
|
|
|
|
2006-11-05 10:59:21 +03:00
|
|
|
/* Load Tx parameters. */
|
|
|
|
ex_setup_tx(sc);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
SET_RX_EARLY_THRESH | ELINK_THRESH_DISABLE);
|
|
|
|
|
|
|
|
bus_space_write_4(iot, ioh, ELINK_DMACTRL,
|
|
|
|
bus_space_read_4(iot, ioh, ELINK_DMACTRL) | ELINK_DMAC_UPRXEAREN);
|
|
|
|
|
2001-12-28 23:35:46 +03:00
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
SET_RD_0_MASK | XL_WATCHED_INTERRUPTS);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
SET_INTR_MASK | XL_WATCHED_INTERRUPTS);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ACK_INTR | 0xff);
|
1999-10-15 10:07:17 +04:00
|
|
|
if (sc->intr_ack)
|
|
|
|
(* sc->intr_ack)(sc);
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_set_media(sc);
|
|
|
|
ex_set_mc(sc);
|
|
|
|
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, STATS_ENABLE);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_ENABLE);
|
|
|
|
bus_space_write_4(iot, ioh, ELINK_UPLISTPTR, sc->sc_upddma);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, RX_ENABLE);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ELINK_UPUNSTALL);
|
|
|
|
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
ex_start(ifp);
|
2005-02-06 06:15:14 +03:00
|
|
|
sc->sc_if_flags = ifp->if_flags;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(1);
|
|
|
|
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_reset(&sc->ex_mii_callout, hz, ex_tick, sc);
|
2000-10-11 20:57:45 +04:00
|
|
|
|
2001-01-30 22:27:39 +03:00
|
|
|
out:
|
|
|
|
if (error) {
|
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
|
|
ifp->if_timer = 0;
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "interface not running\n");
|
2001-01-30 22:27:39 +03:00
|
|
|
}
|
|
|
|
return (error);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
2002-11-09 14:45:18 +03:00
|
|
|
#define MCHASHSIZE 256
|
|
|
|
#define ex_mchash(addr) (ether_crc32_be((addr), ETHER_ADDR_LEN) & \
|
|
|
|
(MCHASHSIZE - 1))
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set multicast receive filter. Also take care of promiscuous mode
|
|
|
|
* here (XXX).
|
|
|
|
*/
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_set_mc(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
struct ethercom *ec = &sc->sc_ethercom;
|
|
|
|
struct ether_multi *enm;
|
|
|
|
struct ether_multistep estep;
|
|
|
|
int i;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t mask = FIL_INDIVIDUAL | FIL_BRDCST;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2002-11-09 14:45:18 +03:00
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
1998-11-04 03:29:28 +03:00
|
|
|
mask |= FIL_PROMISC;
|
2002-11-09 14:45:18 +03:00
|
|
|
goto allmulti;
|
|
|
|
}
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2002-11-09 14:45:18 +03:00
|
|
|
ETHER_FIRST_MULTI(estep, ec, enm);
|
|
|
|
if (enm == NULL)
|
|
|
|
goto nomulti;
|
|
|
|
|
|
|
|
if ((sc->ex_conf & EX_CONF_90XB) == 0)
|
|
|
|
/* No multicast hash filtering. */
|
|
|
|
goto allmulti;
|
|
|
|
|
|
|
|
for (i = 0; i < MCHASHSIZE; i++)
|
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
|
|
|
|
ELINK_COMMAND, ELINK_CLEARHASHFILBIT | i);
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (memcmp(enm->enm_addrlo, enm->enm_addrhi,
|
|
|
|
ETHER_ADDR_LEN) != 0)
|
|
|
|
goto allmulti;
|
|
|
|
|
|
|
|
i = ex_mchash(enm->enm_addrlo);
|
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh,
|
|
|
|
ELINK_COMMAND, ELINK_SETHASHFILBIT | i);
|
|
|
|
ETHER_NEXT_MULTI(estep, enm);
|
|
|
|
} while (enm != NULL);
|
|
|
|
mask |= FIL_MULTIHASH;
|
|
|
|
|
|
|
|
nomulti:
|
|
|
|
ifp->if_flags &= ~IFF_ALLMULTI;
|
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND,
|
|
|
|
SET_RX_FILTER | mask);
|
|
|
|
return;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2002-11-09 14:45:18 +03:00
|
|
|
allmulti:
|
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
|
|
mask |= FIL_MULTICAST;
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND,
|
|
|
|
SET_RX_FILTER | mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-11-05 10:59:21 +03:00
|
|
|
/*
|
|
|
|
* The Tx Complete interrupts occur only on errors,
|
|
|
|
* and this is the error handler.
|
|
|
|
*/
|
1998-11-04 03:29:28 +03:00
|
|
|
static void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_txstat(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2000-10-11 20:57:45 +04:00
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2006-11-05 10:59:21 +03:00
|
|
|
int i, err = 0;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to read+write TX_STATUS until we get a 0 status
|
|
|
|
* in order to turn off the interrupt flag.
|
2006-11-05 10:59:21 +03:00
|
|
|
* ELINK_TXSTATUS is in the upper byte of 2 with ELINK_TIMER.
|
1998-11-04 03:29:28 +03:00
|
|
|
*/
|
2006-11-05 10:59:21 +03:00
|
|
|
for (;;) {
|
|
|
|
i = bus_space_read_2(iot, ioh, ELINK_TIMER);
|
|
|
|
if ((i & TXS_COMPLETE) == 0)
|
|
|
|
break;
|
2005-10-24 00:03:04 +04:00
|
|
|
bus_space_write_2(iot, ioh, ELINK_TIMER, 0x0);
|
2006-11-05 10:59:21 +03:00
|
|
|
err |= i;
|
|
|
|
}
|
|
|
|
err &= ~TXS_TIMER;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2006-11-05 10:59:21 +03:00
|
|
|
if ((err & (TXS_UNDERRUN | TXS_JABBER | TXS_RECLAIM))
|
|
|
|
|| err == 0 /* should not happen, just in case */) {
|
|
|
|
/*
|
|
|
|
* Make sure the transmission is stopped.
|
|
|
|
*/
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ELINK_DNSTALL);
|
|
|
|
for (i = 1000; i > 0; i--)
|
|
|
|
if ((bus_space_read_4(iot, ioh, ELINK_DMACTRL) &
|
|
|
|
ELINK_DMAC_DNINPROG) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the transmitter.
|
|
|
|
*/
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_RESET);
|
|
|
|
|
|
|
|
/* Resetting takes a while and we will do more than wait. */
|
|
|
|
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
++sc->sc_ethercom.ec_if.if_oerrors;
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "%s%s%s",
|
2006-11-05 10:59:21 +03:00
|
|
|
(err & TXS_UNDERRUN) ? " transmit underrun" : "",
|
|
|
|
(err & TXS_JABBER) ? " jabber" : "",
|
|
|
|
(err & TXS_RECLAIM) ? " reclaim" : "");
|
|
|
|
if (err == 0)
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error(" unknown Tx error");
|
2006-11-05 10:59:21 +03:00
|
|
|
printf(" (%x)", err);
|
|
|
|
if (err & TXS_UNDERRUN) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error(" @%d", sc->tx_start_thresh);
|
2006-11-05 10:59:21 +03:00
|
|
|
if (sc->tx_succ_ok < 256 &&
|
|
|
|
(i = min(ETHER_MAX_LEN, sc->tx_start_thresh + 20))
|
|
|
|
> sc->tx_start_thresh) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error(", new threshold is %d", i);
|
2006-11-05 10:59:21 +03:00
|
|
|
sc->tx_start_thresh = i;
|
|
|
|
}
|
1998-11-04 03:29:28 +03:00
|
|
|
sc->tx_succ_ok = 0;
|
2006-11-05 10:59:21 +03:00
|
|
|
}
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error("\n");
|
2006-11-05 10:59:21 +03:00
|
|
|
if (err & TXS_MAX_COLLISION)
|
1998-11-04 03:29:28 +03:00
|
|
|
++sc->sc_ethercom.ec_if.if_collisions;
|
2006-11-05 10:59:21 +03:00
|
|
|
|
|
|
|
/* Wait for TX_RESET to finish. */
|
|
|
|
ex_waitcmd(sc);
|
|
|
|
|
|
|
|
/* Reload Tx parameters. */
|
|
|
|
ex_setup_tx(sc);
|
|
|
|
} else {
|
|
|
|
if (err & TXS_MAX_COLLISION)
|
|
|
|
++sc->sc_ethercom.ec_if.if_collisions;
|
|
|
|
sc->sc_ethercom.ec_if.if_flags &= ~IFF_OACTIVE;
|
2006-11-12 10:07:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_ENABLE);
|
|
|
|
|
|
|
|
/* Retransmit current packet if any. */
|
|
|
|
if (sc->tx_head) {
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
ELINK_DNUNSTALL);
|
|
|
|
bus_space_write_4(iot, ioh, ELINK_DNLISTPTR,
|
|
|
|
DPD_DMADDR(sc, sc->tx_head));
|
|
|
|
|
|
|
|
/* Retrigger watchdog if stopped. */
|
|
|
|
if (ifp->if_timer == 0)
|
|
|
|
ifp->if_timer = 1;
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_media_chg(struct ifnet *ifp)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_UP)
|
2000-10-11 20:57:45 +04:00
|
|
|
ex_init(ifp);
|
1998-11-04 03:29:28 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-05-10 00:54:18 +04:00
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_set_xcvr(struct ex_softc *sc, const uint16_t media)
|
2003-05-10 00:54:18 +04:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint32_t icfg;
|
2003-05-10 00:54:18 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We're already in Window 3
|
|
|
|
*/
|
|
|
|
icfg = bus_space_read_4(iot, ioh, ELINK_W3_INTERNAL_CONFIG);
|
|
|
|
icfg &= ~(CONFIG_XCVR_SEL << 16);
|
|
|
|
if (media & (ELINK_MEDIACAP_MII | ELINK_MEDIACAP_100BASET4))
|
|
|
|
icfg |= ELINKMEDIA_MII << (CONFIG_XCVR_SEL_SHIFT + 16);
|
|
|
|
if (media & ELINK_MEDIACAP_100BASETX)
|
|
|
|
icfg |= ELINKMEDIA_AUTO << (CONFIG_XCVR_SEL_SHIFT + 16);
|
|
|
|
if (media & ELINK_MEDIACAP_100BASEFX)
|
2005-02-27 03:26:58 +03:00
|
|
|
icfg |= ELINKMEDIA_100BASE_FX
|
2003-05-10 00:54:18 +04:00
|
|
|
<< (CONFIG_XCVR_SEL_SHIFT + 16);
|
|
|
|
bus_space_write_4(iot, ioh, ELINK_W3_INTERNAL_CONFIG, icfg);
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_set_media(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint32_t configreg;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
if (((sc->ex_conf & EX_CONF_MII) &&
|
|
|
|
(sc->ex_mii.mii_media_active & IFM_FDX))
|
|
|
|
|| (!(sc->ex_conf & EX_CONF_MII) &&
|
|
|
|
(sc->ex_mii.mii_media.ifm_media & IFM_FDX))) {
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W3_MAC_CONTROL,
|
|
|
|
MAC_CONTROL_FDX);
|
|
|
|
} else {
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W3_MAC_CONTROL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the device has MII, select it, and then tell the
|
|
|
|
* PHY which media to use.
|
|
|
|
*/
|
|
|
|
if (sc->ex_conf & EX_CONF_MII) {
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t val;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2003-05-10 00:54:18 +04:00
|
|
|
GO_WINDOW(3);
|
|
|
|
val = bus_space_read_2(iot, ioh, ELINK_W3_RESET_OPTIONS);
|
|
|
|
ex_set_xcvr(sc, val);
|
1998-11-04 03:29:28 +03:00
|
|
|
mii_mediachg(&sc->ex_mii);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
GO_WINDOW(4);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W4_MEDIA_TYPE, 0);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, STOP_TRANSCEIVER);
|
|
|
|
delay(800);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now turn on the selected media/transceiver.
|
|
|
|
*/
|
|
|
|
switch (IFM_SUBTYPE(sc->ex_mii.mii_media.ifm_cur->ifm_media)) {
|
|
|
|
case IFM_10_T:
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W4_MEDIA_TYPE,
|
|
|
|
JABBER_GUARD_ENABLE|LINKBEAT_ENABLE);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IFM_10_2:
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, START_TRANSCEIVER);
|
|
|
|
DELAY(800);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IFM_100_TX:
|
|
|
|
case IFM_100_FX:
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W4_MEDIA_TYPE,
|
|
|
|
LINKBEAT_ENABLE);
|
|
|
|
DELAY(800);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IFM_10_5:
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W4_MEDIA_TYPE,
|
|
|
|
SQE_ENABLE);
|
|
|
|
DELAY(800);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IFM_MANUAL:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case IFM_NONE:
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("ex_set_media: impossible");
|
|
|
|
}
|
|
|
|
|
|
|
|
GO_WINDOW(3);
|
2000-08-25 13:01:59 +04:00
|
|
|
configreg = bus_space_read_4(iot, ioh, ELINK_W3_INTERNAL_CONFIG);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-08-25 13:01:59 +04:00
|
|
|
configreg &= ~(CONFIG_MEDIAMASK << 16);
|
|
|
|
configreg |= (sc->ex_mii.mii_media.ifm_cur->ifm_data <<
|
|
|
|
(CONFIG_MEDIAMASK_SHIFT + 16));
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-08-25 13:01:59 +04:00
|
|
|
bus_space_write_4(iot, ioh, ELINK_W3_INTERNAL_CONFIG, configreg);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get currently-selected media from card.
|
|
|
|
* (if_media callback, may be called before interface is brought up).
|
|
|
|
*/
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_media_stat(struct ifnet *ifp, struct ifmediareq *req)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t help;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2004-07-02 20:58:36 +04:00
|
|
|
if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) == (IFF_UP|IFF_RUNNING)) {
|
|
|
|
if (sc->ex_conf & EX_CONF_MII) {
|
|
|
|
mii_pollstat(&sc->ex_mii);
|
|
|
|
req->ifm_status = sc->ex_mii.mii_media_status;
|
|
|
|
req->ifm_active = sc->ex_mii.mii_media_active;
|
|
|
|
} else {
|
|
|
|
GO_WINDOW(4);
|
|
|
|
req->ifm_status = IFM_AVALID;
|
|
|
|
req->ifm_active =
|
|
|
|
sc->ex_mii.mii_media.ifm_cur->ifm_media;
|
|
|
|
help = bus_space_read_2(sc->sc_iot, sc->sc_ioh,
|
|
|
|
ELINK_W4_MEDIA_TYPE);
|
|
|
|
if (help & LINKBEAT_DETECT)
|
|
|
|
req->ifm_status |= IFM_ACTIVE;
|
|
|
|
GO_WINDOW(1);
|
|
|
|
}
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Start outputting on the interface.
|
|
|
|
*/
|
|
|
|
static void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_start(struct ifnet *ifp)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
volatile struct ex_fraghdr *fr = NULL;
|
|
|
|
volatile struct ex_dpd *dpd = NULL, *prevdpd = NULL;
|
|
|
|
struct ex_txdesc *txp;
|
2000-12-14 09:27:23 +03:00
|
|
|
struct mbuf *mb_head;
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_t dmamap;
|
2006-11-12 10:16:14 +03:00
|
|
|
int m_csumflags, offset, seglen, totlen, segment, error;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint32_t csum_flags;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
if (sc->tx_head || sc->tx_free == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
txp = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're finished if there is nothing more to add to the list or if
|
|
|
|
* we're all filled up with buffers to transmit.
|
|
|
|
*/
|
2000-12-14 09:27:23 +03:00
|
|
|
while (sc->tx_free != NULL) {
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Grab a packet to transmit.
|
|
|
|
*/
|
2000-12-14 09:27:23 +03:00
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, mb_head);
|
|
|
|
if (mb_head == NULL)
|
|
|
|
break;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2006-11-04 08:18:26 +03:00
|
|
|
/*
|
|
|
|
* mb_head might be updated later,
|
|
|
|
* so preserve csum_flags here.
|
|
|
|
*/
|
|
|
|
m_csumflags = mb_head->m_pkthdr.csum_flags;
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Get pointer to next available tx desc.
|
|
|
|
*/
|
|
|
|
txp = sc->tx_free;
|
|
|
|
dmamap = txp->tx_dmamap;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Go through each of the mbufs in the chain and initialize
|
|
|
|
* the transmit buffer descriptors with the physical address
|
|
|
|
* and size of the mbuf.
|
|
|
|
*/
|
|
|
|
reload:
|
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap,
|
2001-07-19 20:25:23 +04:00
|
|
|
mb_head, BUS_DMA_WRITE|BUS_DMA_NOWAIT);
|
1998-11-04 03:29:28 +03:00
|
|
|
switch (error) {
|
|
|
|
case 0:
|
|
|
|
/* Success. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EFBIG:
|
|
|
|
{
|
|
|
|
struct mbuf *mn;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We ran out of segments. We have to recopy this
|
|
|
|
* mbuf chain first. Bail out if we can't get the
|
|
|
|
* new buffers.
|
|
|
|
*/
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "too many segments, ");
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
MGETHDR(mn, M_DONTWAIT, MT_DATA);
|
|
|
|
if (mn == NULL) {
|
|
|
|
m_freem(mb_head);
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error("aborting\n");
|
1998-11-04 03:29:28 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (mb_head->m_pkthdr.len > MHLEN) {
|
|
|
|
MCLGET(mn, M_DONTWAIT);
|
|
|
|
if ((mn->m_flags & M_EXT) == 0) {
|
|
|
|
m_freem(mn);
|
|
|
|
m_freem(mb_head);
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error("aborting\n");
|
1998-11-04 03:29:28 +03:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_copydata(mb_head, 0, mb_head->m_pkthdr.len,
|
2007-03-04 08:59:00 +03:00
|
|
|
mtod(mn, void *));
|
1998-11-04 03:29:28 +03:00
|
|
|
mn->m_pkthdr.len = mn->m_len = mb_head->m_pkthdr.len;
|
|
|
|
m_freem(mb_head);
|
|
|
|
mb_head = mn;
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error("retrying\n");
|
1998-11-04 03:29:28 +03:00
|
|
|
goto reload;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Some other problem; report it.
|
|
|
|
*/
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"can't load mbuf chain, error = %d\n", error);
|
1998-11-04 03:29:28 +03:00
|
|
|
m_freem(mb_head);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2001-10-01 13:26:29 +04:00
|
|
|
/*
|
|
|
|
* remove our tx desc from freelist.
|
|
|
|
*/
|
|
|
|
sc->tx_free = txp->tx_next;
|
|
|
|
txp->tx_next = NULL;
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
fr = &txp->tx_dpd->dpd_frags[0];
|
|
|
|
totlen = 0;
|
|
|
|
for (segment = 0; segment < dmamap->dm_nsegs; segment++, fr++) {
|
1999-12-12 05:53:56 +03:00
|
|
|
fr->fr_addr = htole32(dmamap->dm_segs[segment].ds_addr);
|
2006-11-12 10:16:14 +03:00
|
|
|
seglen = dmamap->dm_segs[segment].ds_len;
|
|
|
|
fr->fr_len = htole32(seglen);
|
|
|
|
totlen += seglen;
|
|
|
|
}
|
|
|
|
if (__predict_false(totlen <= EX_IP4CSUMTX_PADLEN &&
|
|
|
|
(m_csumflags & M_CSUM_IPv4) != 0)) {
|
|
|
|
/*
|
|
|
|
* Pad short packets to avoid ip4csum-tx bug.
|
|
|
|
*
|
|
|
|
* XXX Should we still consider if such short
|
|
|
|
* (36 bytes or less) packets might already
|
2008-12-03 18:36:11 +03:00
|
|
|
* occupy EX_NTFRAG (== 32) fragments here?
|
2006-11-12 10:16:14 +03:00
|
|
|
*/
|
|
|
|
KASSERT(segment < EX_NTFRAGS);
|
|
|
|
fr->fr_addr = htole32(DPDMEMPAD_DMADDR(sc));
|
|
|
|
seglen = EX_IP4CSUMTX_PADLEN + 1 - totlen;
|
|
|
|
fr->fr_len = htole32(EX_FR_LAST | seglen);
|
|
|
|
totlen += seglen;
|
|
|
|
} else {
|
|
|
|
fr--;
|
|
|
|
fr->fr_len |= htole32(EX_FR_LAST);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
txp->tx_mbhead = mb_head;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
dpd = txp->tx_dpd;
|
|
|
|
dpd->dpd_nextptr = 0;
|
1999-12-12 05:53:56 +03:00
|
|
|
dpd->dpd_fsh = htole32(totlen);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2002-05-12 19:48:36 +04:00
|
|
|
/* Byte-swap constants so compiler can optimize. */
|
2001-06-02 20:17:06 +04:00
|
|
|
|
|
|
|
if (sc->ex_conf & EX_CONF_90XB) {
|
|
|
|
csum_flags = 0;
|
|
|
|
|
2006-11-04 08:18:26 +03:00
|
|
|
if (m_csumflags & M_CSUM_IPv4)
|
2001-06-02 20:17:06 +04:00
|
|
|
csum_flags |= htole32(EX_DPD_IPCKSUM);
|
|
|
|
|
2006-11-04 08:18:26 +03:00
|
|
|
if (m_csumflags & M_CSUM_TCPv4)
|
2001-06-02 20:17:06 +04:00
|
|
|
csum_flags |= htole32(EX_DPD_TCPCKSUM);
|
2006-11-04 08:18:26 +03:00
|
|
|
else if (m_csumflags & M_CSUM_UDPv4)
|
2001-06-02 20:17:06 +04:00
|
|
|
csum_flags |= htole32(EX_DPD_UDPCKSUM);
|
|
|
|
|
|
|
|
dpd->dpd_fsh |= csum_flags;
|
|
|
|
} else {
|
|
|
|
KDASSERT((mb_head->m_pkthdr.csum_flags &
|
|
|
|
(M_CSUM_IPv4|M_CSUM_TCPv4|M_CSUM_UDPv4)) == 0);
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
2005-05-30 08:43:46 +04:00
|
|
|
((const char *)(intptr_t)dpd - (const char *)sc->sc_dpd),
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_dpd),
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No need to stall the download engine, we know it's
|
|
|
|
* not busy right now.
|
|
|
|
*
|
|
|
|
* Fix up pointers in both the "soft" tx and the physical
|
|
|
|
* tx list.
|
|
|
|
*/
|
|
|
|
if (sc->tx_head != NULL) {
|
|
|
|
prevdpd = sc->tx_tail->tx_dpd;
|
2005-05-30 08:43:46 +04:00
|
|
|
offset = ((const char *)(intptr_t)prevdpd - (const char *)sc->sc_dpd);
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
|
|
|
offset, sizeof (struct ex_dpd),
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
1999-12-12 05:53:56 +03:00
|
|
|
prevdpd->dpd_nextptr = htole32(DPD_DMADDR(sc, txp));
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
|
|
|
offset, sizeof (struct ex_dpd),
|
2005-02-27 03:26:58 +03:00
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
1998-11-04 03:29:28 +03:00
|
|
|
sc->tx_tail->tx_next = txp;
|
|
|
|
sc->tx_tail = txp;
|
|
|
|
} else {
|
|
|
|
sc->tx_tail = sc->tx_head = txp;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if NBPFILTER > 0
|
|
|
|
/*
|
|
|
|
* Pass packet to bpf if there is a listener.
|
|
|
|
*/
|
|
|
|
if (ifp->if_bpf)
|
|
|
|
bpf_mtap(ifp->if_bpf, mb_head);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
if (sc->tx_head) {
|
1999-12-12 05:53:56 +03:00
|
|
|
sc->tx_tail->tx_dpd->dpd_fsh |= htole32(EX_DPD_DNIND);
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
((char *)sc->tx_tail->tx_dpd - (char *)sc->sc_dpd),
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_dpd),
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ELINK_DNUNSTALL);
|
|
|
|
bus_space_write_4(iot, ioh, ELINK_DNLISTPTR,
|
|
|
|
DPD_DMADDR(sc, sc->tx_head));
|
1999-01-10 17:19:46 +03:00
|
|
|
|
|
|
|
/* trigger watchdog */
|
|
|
|
ifp->if_timer = 5;
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_intr(void *arg)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = arg;
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t stat;
|
1998-11-04 03:29:28 +03:00
|
|
|
int ret = 0;
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
|
2001-01-30 22:27:39 +03:00
|
|
|
if ((ifp->if_flags & IFF_RUNNING) == 0 ||
|
2008-04-15 00:03:13 +04:00
|
|
|
!device_is_active(sc->sc_dev))
|
2000-03-02 04:21:56 +03:00
|
|
|
return (0);
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
for (;;) {
|
|
|
|
stat = bus_space_read_2(iot, ioh, ELINK_STATUS);
|
2000-01-18 06:35:40 +03:00
|
|
|
|
2001-12-28 23:35:46 +03:00
|
|
|
if ((stat & XL_WATCHED_INTERRUPTS) == 0) {
|
|
|
|
if ((stat & INTR_LATCH) == 0) {
|
2000-01-18 06:35:40 +03:00
|
|
|
#if 0
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"intr latch cleared\n");
|
2000-01-18 06:35:40 +03:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 1;
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Acknowledge interrupts.
|
|
|
|
*/
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ACK_INTR |
|
2001-12-28 23:35:46 +03:00
|
|
|
(stat & (XL_WATCHED_INTERRUPTS | INTR_LATCH)));
|
1999-10-15 10:07:17 +04:00
|
|
|
if (sc->intr_ack)
|
2000-01-18 06:35:40 +03:00
|
|
|
(*sc->intr_ack)(sc);
|
|
|
|
|
2001-12-28 23:35:46 +03:00
|
|
|
if (stat & HOST_ERROR) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"adapter failure (%x)\n", stat);
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_reset(sc);
|
2000-10-11 20:57:45 +04:00
|
|
|
ex_init(ifp);
|
1998-11-04 03:29:28 +03:00
|
|
|
return 1;
|
|
|
|
}
|
2001-12-28 23:35:46 +03:00
|
|
|
if (stat & UPD_STATS) {
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_getstats(sc);
|
|
|
|
}
|
2006-11-12 10:07:39 +03:00
|
|
|
if (stat & TX_COMPLETE) {
|
|
|
|
ex_txstat(sc);
|
|
|
|
#if 0
|
|
|
|
if (stat & DN_COMPLETE)
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"Ignoring Dn interrupt (%x)\n", stat);
|
2006-11-12 10:07:39 +03:00
|
|
|
#endif
|
|
|
|
/*
|
|
|
|
* In some rare cases, both Tx Complete and
|
|
|
|
* Dn Complete bits are set. However, the packet
|
|
|
|
* has been reloaded in ex_txstat() and should not
|
|
|
|
* handle the Dn Complete event here.
|
|
|
|
* Hence the "else" below.
|
|
|
|
*/
|
|
|
|
} else if (stat & DN_COMPLETE) {
|
1998-11-04 03:29:28 +03:00
|
|
|
struct ex_txdesc *txp, *ptxp = NULL;
|
|
|
|
bus_dmamap_t txmap;
|
1999-01-10 17:19:46 +03:00
|
|
|
|
|
|
|
/* reset watchdog timer, was set in ex_start() */
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
for (txp = sc->tx_head; txp != NULL;
|
|
|
|
txp = txp->tx_next) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat,
|
|
|
|
sc->sc_dpd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)txp->tx_dpd - (char *)sc->sc_dpd,
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_dpd),
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
|
|
if (txp->tx_mbhead != NULL) {
|
|
|
|
txmap = txp->tx_dmamap;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, txmap,
|
|
|
|
0, txmap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, txmap);
|
|
|
|
m_freem(txp->tx_mbhead);
|
|
|
|
txp->tx_mbhead = NULL;
|
|
|
|
}
|
|
|
|
ptxp = txp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move finished tx buffers back to the tx free list.
|
|
|
|
*/
|
|
|
|
if (sc->tx_free) {
|
|
|
|
sc->tx_ftail->tx_next = sc->tx_head;
|
|
|
|
sc->tx_ftail = ptxp;
|
|
|
|
} else
|
|
|
|
sc->tx_ftail = sc->tx_free = sc->tx_head;
|
|
|
|
|
|
|
|
sc->tx_head = sc->tx_tail = NULL;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
2006-11-05 10:59:21 +03:00
|
|
|
|
|
|
|
if (sc->tx_succ_ok < 256)
|
|
|
|
sc->tx_succ_ok++;
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
2001-12-28 23:35:46 +03:00
|
|
|
if (stat & UP_COMPLETE) {
|
1998-11-04 03:29:28 +03:00
|
|
|
struct ex_rxdesc *rxd;
|
|
|
|
struct mbuf *m;
|
|
|
|
struct ex_upd *upd;
|
|
|
|
bus_dmamap_t rxmap;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint32_t pktstat;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
rcvloop:
|
|
|
|
rxd = sc->rx_head;
|
|
|
|
rxmap = rxd->rx_dmamap;
|
|
|
|
m = rxd->rx_mbhead;
|
|
|
|
upd = rxd->rx_upd;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, rxmap, 0,
|
|
|
|
rxmap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_upd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
((char *)upd - (char *)sc->sc_upd),
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_upd),
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
2000-05-12 19:22:33 +04:00
|
|
|
pktstat = le32toh(upd->upd_pktstatus);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
if (pktstat & EX_UPD_COMPLETE) {
|
|
|
|
/*
|
|
|
|
* Remove first packet from the chain.
|
|
|
|
*/
|
|
|
|
sc->rx_head = rxd->rx_next;
|
|
|
|
rxd->rx_next = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add a new buffer to the receive chain.
|
|
|
|
* If this fails, the old buffer is recycled
|
|
|
|
* instead.
|
|
|
|
*/
|
|
|
|
if (ex_add_rxbuf(sc, rxd) == 0) {
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t total_len;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-10-17 20:16:52 +04:00
|
|
|
if (pktstat &
|
|
|
|
((sc->sc_ethercom.ec_capenable &
|
|
|
|
ETHERCAP_VLAN_MTU) ?
|
|
|
|
EX_UPD_ERR_VLAN : EX_UPD_ERR)) {
|
1998-11-04 03:29:28 +03:00
|
|
|
ifp->if_ierrors++;
|
|
|
|
m_freem(m);
|
|
|
|
goto rcvloop;
|
|
|
|
}
|
|
|
|
|
|
|
|
total_len = pktstat & EX_UPD_PKTLENMASK;
|
|
|
|
if (total_len <
|
|
|
|
sizeof(struct ether_header)) {
|
|
|
|
m_freem(m);
|
|
|
|
goto rcvloop;
|
|
|
|
}
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
1999-05-19 03:52:51 +04:00
|
|
|
m->m_pkthdr.len = m->m_len = total_len;
|
1998-11-04 03:29:28 +03:00
|
|
|
#if NBPFILTER > 0
|
2000-10-02 03:32:39 +04:00
|
|
|
if (ifp->if_bpf)
|
|
|
|
bpf_mtap(ifp->if_bpf, m);
|
|
|
|
#endif
|
2001-06-02 20:17:06 +04:00
|
|
|
/*
|
|
|
|
* Set the incoming checksum information for the packet.
|
|
|
|
*/
|
|
|
|
if ((sc->ex_conf & EX_CONF_90XB) != 0 &&
|
|
|
|
(pktstat & EX_UPD_IPCHECKED) != 0) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
|
|
|
|
if (pktstat & EX_UPD_IPCKSUMERR)
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_IPv4_BAD;
|
|
|
|
if (pktstat & EX_UPD_TCPCHECKED) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_TCPv4;
|
|
|
|
if (pktstat & EX_UPD_TCPCKSUMERR)
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
M_CSUM_TCP_UDP_BAD;
|
|
|
|
} else if (pktstat & EX_UPD_UDPCHECKED) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_UDPv4;
|
|
|
|
if (pktstat & EX_UPD_UDPCKSUMERR)
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
M_CSUM_TCP_UDP_BAD;
|
|
|
|
}
|
|
|
|
}
|
1999-05-19 03:52:51 +04:00
|
|
|
(*ifp->if_input)(ifp, m);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
goto rcvloop;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Just in case we filled up all UPDs and the DMA engine
|
1999-01-10 17:19:46 +03:00
|
|
|
* stalled. We could be more subtle about this.
|
1998-11-04 03:29:28 +03:00
|
|
|
*/
|
1999-01-10 17:19:46 +03:00
|
|
|
if (bus_space_read_4(iot, ioh, ELINK_UPLISTPTR) == 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"uplistptr was 0\n");
|
2000-10-11 20:57:45 +04:00
|
|
|
ex_init(ifp);
|
1999-01-10 17:19:46 +03:00
|
|
|
} else if (bus_space_read_4(iot, ioh, ELINK_UPPKTSTATUS)
|
|
|
|
& 0x2000) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"receive stalled\n");
|
1999-01-10 17:19:46 +03:00
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND,
|
|
|
|
ELINK_UPUNSTALL);
|
|
|
|
}
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
2003-07-06 13:12:59 +04:00
|
|
|
|
|
|
|
#if NRND > 0
|
|
|
|
if (stat)
|
|
|
|
rnd_add_uint32(&sc->rnd_source, stat);
|
|
|
|
#endif
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
2000-01-18 06:35:40 +03:00
|
|
|
|
|
|
|
/* no more interrupts */
|
2000-12-14 09:27:23 +03:00
|
|
|
if (ret && IFQ_IS_EMPTY(&ifp->if_snd) == 0)
|
2000-01-18 06:35:40 +03:00
|
|
|
ex_start(ifp);
|
1998-11-04 03:29:28 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
*** Summary ***
When a link-layer address changes (e.g., ifconfig ex0 link
02:de:ad:be:ef:02 active), send a gratuitous ARP and/or a Neighbor
Advertisement to update the network-/link-layer address bindings
on our LAN peers.
Refuse a change of ethernet address to the address 00:00:00:00:00:00
or to any multicast/broadcast address. (Thanks matt@.)
Reorder ifnet ioctl operations so that driver ioctls may inherit
the functions of their "class"---ether_ioctl(), fddi_ioctl(), et
cetera---and the class ioctls may inherit from the generic ioctl,
ifioctl_common(), but both driver- and class-ioctls may override
the generic behavior. Make network drivers share more code.
Distinguish a "factory" link-layer address from others for the
purposes of both protecting that address from deletion and computing
EUI64.
Return consistent, appropriate error codes from network drivers.
Improve readability. KNF.
*** Details ***
In if_attach(), always initialize the interface ioctl routine,
ifnet->if_ioctl, if the driver has not already initialized it.
Delete if_ioctl == NULL tests everywhere else, because it cannot
happen.
In the ioctl routines of network interfaces, inherit common ioctl
behaviors by calling either ifioctl_common() or whichever ioctl
routine is appropriate for the class of interface---e.g., ether_ioctl()
for ethernets.
Stop (ab)using SIOCSIFADDR and start to use SIOCINITIFADDR. In
the user->kernel interface, SIOCSIFADDR's argument was an ifreq,
but on the protocol->ifnet interface, SIOCSIFADDR's argument was
an ifaddr. That was confusing, and it would work against me as I
make it possible for a network interface to overload most ioctls.
On the protocol->ifnet interface, replace SIOCSIFADDR with
SIOCINITIFADDR. In ifioctl(), return EPERM if userland tries to
invoke SIOCINITIFADDR.
In ifioctl(), give the interface the first shot at handling most
interface ioctls, and give the protocol the second shot, instead
of the other way around. Finally, let compatibility code (COMPAT_OSOCK)
take a shot.
Pull device initialization out of switch statements under
SIOCINITIFADDR. For example, pull ..._init() out of any switch
statement that looks like this:
switch (...->sa_family) {
case ...:
..._init();
...
break;
...
default:
..._init();
...
break;
}
Rewrite many if-else clauses that handle all permutations of IFF_UP
and IFF_RUNNING to use a switch statement,
switch (x & (IFF_UP|IFF_RUNNING)) {
case 0:
...
break;
case IFF_RUNNING:
...
break;
case IFF_UP:
...
break;
case IFF_UP|IFF_RUNNING:
...
break;
}
unifdef lots of code containing #ifdef FreeBSD, #ifdef NetBSD, and
#ifdef SIOCSIFMTU, especially in fwip(4) and in ndis(4).
In ipw(4), remove an if_set_sadl() call that is out of place.
In nfe(4), reuse the jumbo MTU logic in ether_ioctl().
Let ethernets register a callback for setting h/w state such as
promiscuous mode and the multicast filter in accord with a change
in the if_flags: ether_set_ifflags_cb() registers a callback that
returns ENETRESET if the caller should reset the ethernet by calling
if_init(), 0 on success, != 0 on failure. Pull common code from
ex(4), gem(4), nfe(4), sip(4), tlp(4), vge(4) into ether_ioctl(),
and register if_flags callbacks for those drivers.
Return ENOTTY instead of EINVAL for inappropriate ioctls. In
zyd(4), use ENXIO instead of ENOTTY to indicate that the device is
not any longer attached.
Add to if_set_sadl() a boolean 'factory' argument that indicates
whether a link-layer address was assigned by the factory or some
other source. In a comment, recommend using the factory address
for generating an EUI64, and update in6_get_hw_ifid() to prefer a
factory address to any other link-layer address.
Add a routing message, RTM_LLINFO_UPD, that tells protocols to
update the binding of network-layer addresses to link-layer addresses.
Implement this message in IPv4 and IPv6 by sending a gratuitous
ARP or a neighbor advertisement, respectively. Generate RTM_LLINFO_UPD
messages on a change of an interface's link-layer address.
In ether_ioctl(), do not let SIOCALIFADDR set a link-layer address
that is broadcast/multicast or equal to 00:00:00:00:00:00.
Make ether_ioctl() call ifioctl_common() to handle ioctls that it
does not understand.
In gif(4), initialize if_softc and use it, instead of assuming that
the gif_softc and ifp overlap.
Let ifioctl_common() handle SIOCGIFADDR.
Sprinkle rtcache_invariants(), which checks on DIAGNOSTIC kernels
that certain invariants on a struct route are satisfied.
In agr(4), rewrite agr_ioctl_filter() to be a bit more explicit
about the ioctls that we do not allow on an agr(4) member interface.
bzero -> memset. Delete unnecessary casts to void *. Use
sockaddr_in_init() and sockaddr_in6_init(). Compare pointers with
NULL instead of "testing truth". Replace some instances of (type
*)0 with NULL. Change some K&R prototypes to ANSI C, and join
lines.
2008-11-07 03:20:01 +03:00
|
|
|
static int
|
|
|
|
ex_ifflags_cb(struct ethercom *ec)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = &ec->ec_if;
|
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
|
|
|
int change = ifp->if_flags ^ sc->sc_if_flags;
|
|
|
|
|
|
|
|
if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0)
|
|
|
|
return ENETRESET;
|
|
|
|
else if ((change & IFF_PROMISC) != 0)
|
|
|
|
ex_set_mc(sc);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
|
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
2000-10-11 20:57:45 +04:00
|
|
|
int s, error;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SIOCSIFMEDIA:
|
|
|
|
case SIOCGIFMEDIA:
|
|
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->ex_mii.mii_media, cmd);
|
|
|
|
break;
|
2000-10-11 20:57:45 +04:00
|
|
|
default:
|
2008-02-07 04:21:52 +03:00
|
|
|
if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
|
|
|
|
break;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
|
|
|
|
;
|
|
|
|
else if (ifp->if_flags & IFF_RUNNING) {
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Multicast list has changed; set the hardware filter
|
|
|
|
* accordingly.
|
|
|
|
*/
|
2008-02-07 04:21:52 +03:00
|
|
|
ex_set_mc(sc);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-02-06 06:15:14 +03:00
|
|
|
sc->sc_if_flags = ifp->if_flags;
|
1998-11-04 03:29:28 +03:00
|
|
|
splx(s);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_getstats(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint8_t upperok;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(6);
|
|
|
|
upperok = bus_space_read_1(iot, ioh, UPPER_FRAMES_OK);
|
|
|
|
ifp->if_ipackets += bus_space_read_1(iot, ioh, RX_FRAMES_OK);
|
|
|
|
ifp->if_ipackets += (upperok & 0x03) << 8;
|
|
|
|
ifp->if_opackets += bus_space_read_1(iot, ioh, TX_FRAMES_OK);
|
|
|
|
ifp->if_opackets += (upperok & 0x30) << 4;
|
|
|
|
ifp->if_ierrors += bus_space_read_1(iot, ioh, RX_OVERRUNS);
|
|
|
|
ifp->if_collisions += bus_space_read_1(iot, ioh, TX_COLLISIONS);
|
|
|
|
/*
|
|
|
|
* There seems to be no way to get the exact number of collisions,
|
2001-09-16 20:34:23 +04:00
|
|
|
* this is the number that occurred at the very least.
|
1998-11-04 03:29:28 +03:00
|
|
|
*/
|
|
|
|
ifp->if_collisions += 2 * bus_space_read_1(iot, ioh,
|
|
|
|
TX_AFTER_X_COLLISIONS);
|
2002-04-06 23:28:01 +04:00
|
|
|
/*
|
|
|
|
* Interface byte counts are counted by ether_input() and
|
|
|
|
* ether_output(), so don't accumulate them here. Just
|
|
|
|
* read the NIC counters so they don't generate overflow interrupts.
|
|
|
|
* Upper byte counters are latched from reading the totals, so
|
|
|
|
* they don't need to be read if we don't need their values.
|
|
|
|
*/
|
2006-05-22 03:56:09 +04:00
|
|
|
(void)bus_space_read_2(iot, ioh, RX_TOTAL_OK);
|
|
|
|
(void)bus_space_read_2(iot, ioh, TX_TOTAL_OK);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear the following to avoid stats overflow interrupts
|
|
|
|
*/
|
2006-05-22 03:56:09 +04:00
|
|
|
(void)bus_space_read_1(iot, ioh, TX_DEFERRALS);
|
|
|
|
(void)bus_space_read_1(iot, ioh, TX_AFTER_1_COLLISION);
|
|
|
|
(void)bus_space_read_1(iot, ioh, TX_NO_SQE);
|
|
|
|
(void)bus_space_read_1(iot, ioh, TX_CD_LOST);
|
1998-11-04 03:29:28 +03:00
|
|
|
GO_WINDOW(4);
|
2006-05-22 03:56:09 +04:00
|
|
|
(void)bus_space_read_1(iot, ioh, ELINK_W4_BADSSD);
|
1998-11-04 03:29:28 +03:00
|
|
|
GO_WINDOW(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_printstats(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
|
|
|
|
ex_getstats(sc);
|
1999-11-19 13:42:48 +03:00
|
|
|
printf("in %llu out %llu ierror %llu oerror %llu ibytes %llu obytes "
|
|
|
|
"%llu\n", (unsigned long long)ifp->if_ipackets,
|
|
|
|
(unsigned long long)ifp->if_opackets,
|
|
|
|
(unsigned long long)ifp->if_ierrors,
|
|
|
|
(unsigned long long)ifp->if_oerrors,
|
|
|
|
(unsigned long long)ifp->if_ibytes,
|
|
|
|
(unsigned long long)ifp->if_obytes);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_tick(void *arg)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = arg;
|
2000-03-02 04:21:56 +03:00
|
|
|
int s;
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
if (!device_is_active(sc->sc_dev))
|
2000-03-02 04:21:56 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
s = splnet();
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
if (sc->ex_conf & EX_CONF_MII)
|
|
|
|
mii_tick(&sc->ex_mii);
|
|
|
|
|
|
|
|
if (!(bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, ELINK_STATUS)
|
2001-12-28 23:35:46 +03:00
|
|
|
& COMMAND_IN_PROGRESS))
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_getstats(sc);
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_reset(&sc->ex_mii_callout, hz, ex_tick, sc);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_reset(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t val = GLOBAL_RESET;
|
2000-09-19 05:15:06 +04:00
|
|
|
|
|
|
|
if (sc->ex_conf & EX_CONF_RESETHACK)
|
2001-05-11 02:57:44 +04:00
|
|
|
val |= 0x10;
|
2000-09-19 05:15:06 +04:00
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_COMMAND, val);
|
2001-05-11 02:57:44 +04:00
|
|
|
/*
|
|
|
|
* XXX apparently the command in progress bit can't be trusted
|
|
|
|
* during a reset, so we just always wait this long. Fortunately
|
|
|
|
* we normally only reset the chip during autoconfig.
|
|
|
|
*/
|
|
|
|
delay(100000);
|
1998-11-04 03:29:28 +03:00
|
|
|
ex_waitcmd(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_watchdog(struct ifnet *ifp)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
log(LOG_ERR, "%s: device timeout\n", device_xname(sc->sc_dev));
|
1998-11-04 03:29:28 +03:00
|
|
|
++sc->sc_ethercom.ec_if.if_oerrors;
|
|
|
|
|
|
|
|
ex_reset(sc);
|
2000-10-11 20:57:45 +04:00
|
|
|
ex_init(ifp);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_stop(struct ifnet *ifp, int disable)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2000-10-11 20:57:45 +04:00
|
|
|
struct ex_softc *sc = ifp->if_softc;
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
struct ex_txdesc *tx;
|
|
|
|
struct ex_rxdesc *rx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, RX_DISABLE);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, TX_DISABLE);
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, STOP_TRANSCEIVER);
|
|
|
|
|
|
|
|
for (tx = sc->tx_head ; tx != NULL; tx = tx->tx_next) {
|
|
|
|
if (tx->tx_mbhead == NULL)
|
|
|
|
continue;
|
|
|
|
m_freem(tx->tx_mbhead);
|
|
|
|
tx->tx_mbhead = NULL;
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, tx->tx_dmamap);
|
|
|
|
tx->tx_dpd->dpd_fsh = tx->tx_dpd->dpd_nextptr = 0;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dpd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
((char *)tx->tx_dpd - (char *)sc->sc_dpd),
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_dpd),
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
}
|
|
|
|
sc->tx_tail = sc->tx_head = NULL;
|
|
|
|
ex_init_txdescs(sc);
|
|
|
|
|
|
|
|
sc->rx_tail = sc->rx_head = 0;
|
|
|
|
for (i = 0; i < EX_NUPD; i++) {
|
|
|
|
rx = &sc->sc_rxdescs[i];
|
|
|
|
if (rx->rx_mbhead != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, rx->rx_dmamap);
|
|
|
|
m_freem(rx->rx_mbhead);
|
|
|
|
rx->rx_mbhead = NULL;
|
|
|
|
}
|
|
|
|
ex_add_rxbuf(sc, rx);
|
|
|
|
}
|
|
|
|
|
2001-12-28 23:35:46 +03:00
|
|
|
bus_space_write_2(iot, ioh, ELINK_COMMAND, ACK_INTR | INTR_LATCH);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_stop(&sc->ex_mii_callout);
|
1999-11-12 21:14:17 +03:00
|
|
|
if (sc->ex_conf & EX_CONF_MII)
|
|
|
|
mii_down(&sc->ex_mii);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
if (disable)
|
2001-01-30 22:27:39 +03:00
|
|
|
ex_disable(sc);
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
2005-02-06 06:15:14 +03:00
|
|
|
sc->sc_if_flags = ifp->if_flags;
|
1998-11-04 03:29:28 +03:00
|
|
|
ifp->if_timer = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_init_txdescs(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < EX_NDPD; i++) {
|
|
|
|
sc->sc_txdescs[i].tx_dmamap = sc->sc_tx_dmamaps[i];
|
|
|
|
sc->sc_txdescs[i].tx_dpd = &sc->sc_dpd[i];
|
|
|
|
if (i < EX_NDPD - 1)
|
|
|
|
sc->sc_txdescs[i].tx_next = &sc->sc_txdescs[i + 1];
|
|
|
|
else
|
|
|
|
sc->sc_txdescs[i].tx_next = NULL;
|
|
|
|
}
|
|
|
|
sc->tx_free = &sc->sc_txdescs[0];
|
|
|
|
sc->tx_ftail = &sc->sc_txdescs[EX_NDPD-1];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2000-02-05 21:11:55 +03:00
|
|
|
int
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_activate(device_t self, enum devact act)
|
2000-02-05 21:11:55 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(self);
|
2000-02-05 21:11:55 +03:00
|
|
|
|
|
|
|
switch (act) {
|
|
|
|
case DVACT_DEACTIVATE:
|
|
|
|
if_deactivate(&sc->sc_ethercom.ec_if);
|
2009-09-15 23:20:29 +04:00
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return EOPNOTSUPP;
|
2000-02-05 21:11:55 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_detach(struct ex_softc *sc)
|
2000-02-05 21:11:55 +03:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
struct ex_rxdesc *rxd;
|
|
|
|
int i;
|
|
|
|
|
2000-05-29 21:37:12 +04:00
|
|
|
/* Succeed now if there's no work to do. */
|
|
|
|
if ((sc->ex_flags & EX_FLAGS_ATTACHED) == 0)
|
|
|
|
return (0);
|
|
|
|
|
2000-02-05 21:11:55 +03:00
|
|
|
/* Unhook our tick handler. */
|
2000-03-23 10:01:25 +03:00
|
|
|
callout_stop(&sc->ex_mii_callout);
|
2000-02-05 21:11:55 +03:00
|
|
|
|
2000-03-01 23:51:50 +03:00
|
|
|
if (sc->ex_conf & EX_CONF_MII) {
|
|
|
|
/* Detach all PHYs */
|
|
|
|
mii_detach(&sc->ex_mii, MII_PHY_ANY, MII_OFFSET_ANY);
|
|
|
|
}
|
2000-02-05 21:11:55 +03:00
|
|
|
|
|
|
|
/* Delete all remaining media. */
|
|
|
|
ifmedia_delete_instance(&sc->ex_mii.mii_media, IFM_INST_ANY);
|
|
|
|
|
|
|
|
#if NRND > 0
|
|
|
|
rnd_detach_source(&sc->rnd_source);
|
|
|
|
#endif
|
|
|
|
ether_ifdetach(ifp);
|
|
|
|
if_detach(ifp);
|
|
|
|
|
|
|
|
for (i = 0; i < EX_NUPD; i++) {
|
|
|
|
rxd = &sc->sc_rxdescs[i];
|
|
|
|
if (rxd->rx_mbhead != NULL) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, rxd->rx_dmamap);
|
|
|
|
m_freem(rxd->rx_mbhead);
|
|
|
|
rxd->rx_mbhead = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < EX_NUPD; i++)
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_rx_dmamaps[i]);
|
|
|
|
for (i = 0; i < EX_NDPD; i++)
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_tx_dmamaps[i]);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dpd_dmamap);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dpd_dmamap);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_dpd,
|
2000-02-05 21:11:55 +03:00
|
|
|
EX_NDPD * sizeof (struct ex_dpd));
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &sc->sc_dseg, sc->sc_drseg);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_upd_dmamap);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_upd_dmamap);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)sc->sc_upd,
|
2000-02-05 21:11:55 +03:00
|
|
|
EX_NUPD * sizeof (struct ex_upd));
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &sc->sc_useg, sc->sc_urseg);
|
|
|
|
|
2008-04-15 00:03:13 +04:00
|
|
|
pmf_device_deregister(sc->sc_dev);
|
2000-02-05 21:11:55 +03:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
/*
|
|
|
|
* Before reboots, reset card completely.
|
|
|
|
*/
|
2008-04-15 00:03:13 +04:00
|
|
|
static bool
|
|
|
|
ex_shutdown(device_t self, int flags)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(self);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
2001-01-30 22:27:39 +03:00
|
|
|
ex_stop(&sc->sc_ethercom.ec_if, 1);
|
2002-07-01 20:16:37 +04:00
|
|
|
/*
|
|
|
|
* Make sure the interface is powered up when we reboot,
|
|
|
|
* otherwise firmware on some systems gets really confused.
|
|
|
|
*/
|
|
|
|
(void) ex_enable(sc);
|
2008-04-15 00:03:13 +04:00
|
|
|
return true;
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read EEPROM data.
|
|
|
|
* XXX what to do if EEPROM doesn't unbusy?
|
|
|
|
*/
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t
|
|
|
|
ex_read_eeprom(struct ex_softc *sc, int offset)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
2008-04-14 14:54:21 +04:00
|
|
|
uint16_t data = 0, cmd = READ_EEPROM;
|
2000-09-19 05:15:06 +04:00
|
|
|
int off;
|
|
|
|
|
|
|
|
off = sc->ex_conf & EX_CONF_EEPROM_OFF ? 0x30 : 0;
|
|
|
|
cmd = sc->ex_conf & EX_CONF_EEPROM_8BIT ? READ_EEPROM8 : READ_EEPROM;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(0);
|
|
|
|
if (ex_eeprom_busy(sc))
|
|
|
|
goto out;
|
2000-09-19 05:15:06 +04:00
|
|
|
bus_space_write_2(iot, ioh, ELINK_W0_EEPROM_COMMAND,
|
|
|
|
cmd | (off + (offset & 0x3f)));
|
1998-11-04 03:29:28 +03:00
|
|
|
if (ex_eeprom_busy(sc))
|
|
|
|
goto out;
|
|
|
|
data = bus_space_read_2(iot, ioh, ELINK_W0_EEPROM_DATA);
|
|
|
|
out:
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_eeprom_busy(struct ex_softc *sc)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int i = 100;
|
|
|
|
|
|
|
|
while (i--) {
|
|
|
|
if (!(bus_space_read_2(iot, ioh, ELINK_W0_EEPROM_COMMAND) &
|
|
|
|
EEPROM_BUSY))
|
|
|
|
return 0;
|
|
|
|
delay(100);
|
|
|
|
}
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "eeprom stays busy.\n");
|
1998-11-04 03:29:28 +03:00
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new rx buffer and add it to the 'soft' rx list.
|
|
|
|
*/
|
|
|
|
static int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_add_rxbuf(struct ex_softc *sc, struct ex_rxdesc *rxd)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
|
|
|
struct mbuf *m, *oldm;
|
|
|
|
bus_dmamap_t rxmap;
|
|
|
|
int error, rval = 0;
|
|
|
|
|
|
|
|
oldm = rxd->rx_mbhead;
|
|
|
|
rxmap = rxd->rx_dmamap;
|
|
|
|
|
|
|
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (m != NULL) {
|
|
|
|
MCLGET(m, M_DONTWAIT);
|
|
|
|
if ((m->m_flags & M_EXT) == 0) {
|
|
|
|
m_freem(m);
|
|
|
|
if (oldm == NULL)
|
|
|
|
return 1;
|
|
|
|
m = oldm;
|
2004-09-28 15:35:21 +04:00
|
|
|
MRESETDATA(m);
|
1998-11-04 03:29:28 +03:00
|
|
|
rval = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (oldm == NULL)
|
|
|
|
return 1;
|
|
|
|
m = oldm;
|
2004-09-28 15:35:21 +04:00
|
|
|
MRESETDATA(m);
|
1998-11-04 03:29:28 +03:00
|
|
|
rval = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Setup the DMA map for this receive buffer.
|
|
|
|
*/
|
|
|
|
if (m != oldm) {
|
|
|
|
if (oldm != NULL)
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, rxmap);
|
|
|
|
error = bus_dmamap_load(sc->sc_dmat, rxmap,
|
2001-07-19 20:25:23 +04:00
|
|
|
m->m_ext.ext_buf, MCLBYTES, NULL,
|
|
|
|
BUS_DMA_READ|BUS_DMA_NOWAIT);
|
1998-11-04 03:29:28 +03:00
|
|
|
if (error) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "can't load rx buffer, error = %d\n",
|
2008-04-08 16:07:25 +04:00
|
|
|
error);
|
1998-11-04 03:29:28 +03:00
|
|
|
panic("ex_add_rxbuf"); /* XXX */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Align for data after 14 byte header.
|
|
|
|
*/
|
|
|
|
m->m_data += 2;
|
|
|
|
|
|
|
|
rxd->rx_mbhead = m;
|
1999-12-12 05:53:56 +03:00
|
|
|
rxd->rx_upd->upd_pktstatus = htole32(MCLBYTES - 2);
|
1999-04-27 03:28:31 +04:00
|
|
|
rxd->rx_upd->upd_frags[0].fr_addr =
|
1999-12-12 05:53:56 +03:00
|
|
|
htole32(rxmap->dm_segs[0].ds_addr + 2);
|
1998-11-04 03:29:28 +03:00
|
|
|
rxd->rx_upd->upd_nextptr = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach it to the end of the list.
|
|
|
|
*/
|
|
|
|
if (sc->rx_head != NULL) {
|
|
|
|
sc->rx_tail->rx_next = rxd;
|
1999-12-12 05:53:56 +03:00
|
|
|
sc->rx_tail->rx_upd->upd_nextptr = htole32(sc->sc_upddma +
|
2007-03-04 08:59:00 +03:00
|
|
|
((char *)rxd->rx_upd - (char *)sc->sc_upd));
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_upd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)sc->rx_tail->rx_upd - (char *)sc->sc_upd,
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_upd),
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
} else {
|
|
|
|
sc->rx_head = rxd;
|
|
|
|
}
|
|
|
|
sc->rx_tail = rxd;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, rxmap, 0, rxmap->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_upd_dmamap,
|
2007-03-04 08:59:00 +03:00
|
|
|
((char *)rxd->rx_upd - (char *)sc->sc_upd),
|
1998-11-04 03:29:28 +03:00
|
|
|
sizeof (struct ex_upd), BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
return (rval);
|
|
|
|
}
|
|
|
|
|
2008-04-14 14:54:21 +04:00
|
|
|
uint32_t
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_mii_bitbang_read(device_t self)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(self);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
/* We're already in Window 4. */
|
|
|
|
return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, ELINK_W4_PHYSMGMT));
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_mii_bitbang_write(device_t self, uint32_t val)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(self);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
/* We're already in Window 4. */
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_write_2(sc->sc_iot, sc->sc_ioh, ELINK_W4_PHYSMGMT, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_mii_readreg(device_t v, int phy, int reg)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(v);
|
1999-11-17 20:56:52 +03:00
|
|
|
int val;
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
if ((sc->ex_conf & EX_CONF_INTPHY) && phy != ELINK_INTPHY_ID)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
GO_WINDOW(4);
|
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
val = mii_bitbang_readreg(v, &ex_mii_bitbang_ops, phy, reg);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(1);
|
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
return (val);
|
1998-11-04 03:29:28 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_mii_writereg(device_t v, int phy, int reg, int data)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(v);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(4);
|
|
|
|
|
1999-11-17 20:56:52 +03:00
|
|
|
mii_bitbang_writereg(v, &ex_mii_bitbang_ops, phy, reg, data);
|
1998-11-04 03:29:28 +03:00
|
|
|
|
|
|
|
GO_WINDOW(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-15 00:03:13 +04:00
|
|
|
ex_mii_statchg(device_t v)
|
1998-11-04 03:29:28 +03:00
|
|
|
{
|
2008-04-15 00:03:13 +04:00
|
|
|
struct ex_softc *sc = device_private(v);
|
1998-11-04 03:29:28 +03:00
|
|
|
bus_space_tag_t iot = sc->sc_iot;
|
|
|
|
bus_space_handle_t ioh = sc->sc_ioh;
|
|
|
|
int mctl;
|
2005-02-27 03:26:58 +03:00
|
|
|
|
1998-11-04 03:29:28 +03:00
|
|
|
GO_WINDOW(3);
|
|
|
|
mctl = bus_space_read_2(iot, ioh, ELINK_W3_MAC_CONTROL);
|
|
|
|
if (sc->ex_mii.mii_media_active & IFM_FDX)
|
|
|
|
mctl |= MAC_CONTROL_FDX;
|
|
|
|
else
|
|
|
|
mctl &= ~MAC_CONTROL_FDX;
|
|
|
|
bus_space_write_2(iot, ioh, ELINK_W3_MAC_CONTROL, mctl);
|
|
|
|
GO_WINDOW(1); /* back to operating window */
|
|
|
|
}
|
2001-01-30 22:27:39 +03:00
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
int
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_enable(struct ex_softc *sc)
|
2001-01-30 22:27:39 +03:00
|
|
|
{
|
|
|
|
if (sc->enabled == 0 && sc->enable != NULL) {
|
|
|
|
if ((*sc->enable)(sc) != 0) {
|
2008-04-15 00:03:13 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "device enable failed\n");
|
2001-01-30 22:27:39 +03:00
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
sc->enabled = 1;
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2005-02-27 03:26:58 +03:00
|
|
|
void
|
2008-04-14 14:54:21 +04:00
|
|
|
ex_disable(struct ex_softc *sc)
|
2001-01-30 22:27:39 +03:00
|
|
|
{
|
|
|
|
if (sc->enabled == 1 && sc->disable != NULL) {
|
|
|
|
(*sc->disable)(sc);
|
|
|
|
sc->enabled = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|