2012-01-30 23:41:18 +04:00
|
|
|
/* $NetBSD: if_nfe.c,v 1.55 2012/01/30 19:41:20 drochner Exp $ */
|
2008-04-17 23:12:26 +04:00
|
|
|
/* $OpenBSD: if_nfe.c,v 1.77 2008/02/05 16:52:50 brad Exp $ */
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
/*-
|
2008-04-17 23:12:26 +04:00
|
|
|
* Copyright (c) 2006, 2007 Damien Bergamini <damien.bergamini@free.fr>
|
2006-03-13 01:40:42 +03:00
|
|
|
* Copyright (c) 2005, 2006 Jonathan Gray <jsg@openbsd.org>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice appear in all copies.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Driver for NVIDIA nForce MCP Fast Ethernet and Gigabit Ethernet */
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
2012-01-30 23:41:18 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: if_nfe.c,v 1.55 2012/01/30 19:41:20 drochner Exp $");
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
#include "opt_inet.h"
|
|
|
|
#include "vlan.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/endian.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/mbuf.h>
|
2008-04-20 12:57:37 +04:00
|
|
|
#include <sys/mutex.h>
|
2006-03-13 01:40:42 +03:00
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/device.h>
|
2008-04-17 23:12:26 +04:00
|
|
|
#include <sys/callout.h>
|
2006-03-13 01:40:42 +03:00
|
|
|
#include <sys/socket.h>
|
|
|
|
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/bus.h>
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/if_ether.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
|
|
|
|
#ifdef INET
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/in_systm.h>
|
|
|
|
#include <netinet/in_var.h>
|
|
|
|
#include <netinet/ip.h>
|
|
|
|
#include <netinet/if_inarp.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if NVLAN > 0
|
|
|
|
#include <net/if_types.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
|
|
|
#include <dev/mii/mii.h>
|
|
|
|
#include <dev/mii/miivar.h>
|
|
|
|
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
|
|
|
|
#include <dev/pci/if_nfereg.h>
|
|
|
|
#include <dev/pci/if_nfevar.h>
|
|
|
|
|
*** 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 nfe_ifflags_cb(struct ethercom *);
|
|
|
|
|
2008-03-26 17:46:21 +03:00
|
|
|
int nfe_match(device_t, cfdata_t, void *);
|
|
|
|
void nfe_attach(device_t, device_t, void *);
|
2010-11-03 17:03:40 +03:00
|
|
|
int nfe_detach(device_t, int);
|
2006-03-13 01:40:42 +03:00
|
|
|
void nfe_power(int, void *);
|
2008-03-26 17:46:21 +03:00
|
|
|
void nfe_miibus_statchg(device_t);
|
|
|
|
int nfe_miibus_readreg(device_t, int, int);
|
|
|
|
void nfe_miibus_writereg(device_t, int, int, int);
|
2006-03-13 01:40:42 +03:00
|
|
|
int nfe_intr(void *);
|
2007-03-04 08:59:00 +03:00
|
|
|
int nfe_ioctl(struct ifnet *, u_long, void *);
|
2006-03-13 01:40:42 +03:00
|
|
|
void nfe_txdesc32_sync(struct nfe_softc *, struct nfe_desc32 *, int);
|
|
|
|
void nfe_txdesc64_sync(struct nfe_softc *, struct nfe_desc64 *, int);
|
|
|
|
void nfe_txdesc32_rsync(struct nfe_softc *, int, int, int);
|
|
|
|
void nfe_txdesc64_rsync(struct nfe_softc *, int, int, int);
|
|
|
|
void nfe_rxdesc32_sync(struct nfe_softc *, struct nfe_desc32 *, int);
|
|
|
|
void nfe_rxdesc64_sync(struct nfe_softc *, struct nfe_desc64 *, int);
|
|
|
|
void nfe_rxeof(struct nfe_softc *);
|
|
|
|
void nfe_txeof(struct nfe_softc *);
|
|
|
|
int nfe_encap(struct nfe_softc *, struct mbuf *);
|
|
|
|
void nfe_start(struct ifnet *);
|
|
|
|
void nfe_watchdog(struct ifnet *);
|
|
|
|
int nfe_init(struct ifnet *);
|
|
|
|
void nfe_stop(struct ifnet *, int);
|
2007-09-24 17:17:53 +04:00
|
|
|
struct nfe_jbuf *nfe_jalloc(struct nfe_softc *, int);
|
2007-03-04 08:59:00 +03:00
|
|
|
void nfe_jfree(struct mbuf *, void *, size_t, void *);
|
2006-03-13 01:40:42 +03:00
|
|
|
int nfe_jpool_alloc(struct nfe_softc *);
|
|
|
|
void nfe_jpool_free(struct nfe_softc *);
|
|
|
|
int nfe_alloc_rx_ring(struct nfe_softc *, struct nfe_rx_ring *);
|
|
|
|
void nfe_reset_rx_ring(struct nfe_softc *, struct nfe_rx_ring *);
|
|
|
|
void nfe_free_rx_ring(struct nfe_softc *, struct nfe_rx_ring *);
|
|
|
|
int nfe_alloc_tx_ring(struct nfe_softc *, struct nfe_tx_ring *);
|
|
|
|
void nfe_reset_tx_ring(struct nfe_softc *, struct nfe_tx_ring *);
|
|
|
|
void nfe_free_tx_ring(struct nfe_softc *, struct nfe_tx_ring *);
|
|
|
|
void nfe_setmulti(struct nfe_softc *);
|
|
|
|
void nfe_get_macaddr(struct nfe_softc *, uint8_t *);
|
|
|
|
void nfe_set_macaddr(struct nfe_softc *, const uint8_t *);
|
|
|
|
void nfe_tick(void *);
|
2008-05-26 02:57:35 +04:00
|
|
|
void nfe_poweron(device_t);
|
2010-02-25 01:37:54 +03:00
|
|
|
bool nfe_resume(device_t, const pmf_qual_t *);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2010-11-03 17:03:40 +03:00
|
|
|
CFATTACH_DECL_NEW(nfe, sizeof(struct nfe_softc),
|
|
|
|
nfe_match, nfe_attach, nfe_detach, NULL);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2008-04-20 12:57:37 +04:00
|
|
|
/* #define NFE_NO_JUMBO */
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
#ifdef NFE_DEBUG
|
|
|
|
int nfedebug = 0;
|
|
|
|
#define DPRINTF(x) do { if (nfedebug) printf x; } while (0)
|
|
|
|
#define DPRINTFN(n,x) do { if (nfedebug >= (n)) printf x; } while (0)
|
|
|
|
#else
|
|
|
|
#define DPRINTF(x)
|
|
|
|
#define DPRINTFN(n,x)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* deal with naming differences */
|
|
|
|
|
|
|
|
#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN2 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN1
|
|
|
|
#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN3 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE2_400_LAN2
|
|
|
|
#define PCI_PRODUCT_NVIDIA_NFORCE3_LAN5 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE3_250_LAN
|
|
|
|
|
|
|
|
#define PCI_PRODUCT_NVIDIA_CK804_LAN1 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE4_LAN1
|
|
|
|
#define PCI_PRODUCT_NVIDIA_CK804_LAN2 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE4_LAN2
|
|
|
|
|
|
|
|
#define PCI_PRODUCT_NVIDIA_MCP51_LAN1 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE430_LAN1
|
|
|
|
#define PCI_PRODUCT_NVIDIA_MCP51_LAN2 \
|
|
|
|
PCI_PRODUCT_NVIDIA_NFORCE430_LAN2
|
|
|
|
|
|
|
|
#ifdef _LP64
|
|
|
|
#define __LP64__ 1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const struct nfe_product {
|
|
|
|
pci_vendor_id_t vendor;
|
|
|
|
pci_product_id_t product;
|
|
|
|
} nfe_devices[] = {
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE_LAN },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE2_LAN },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN3 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN4 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_NFORCE3_LAN5 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_CK804_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_CK804_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP04_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP51_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP51_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP55_LAN1 },
|
2006-09-03 11:42:04 +04:00
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP55_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN3 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP61_LAN4 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN3 },
|
2007-11-14 15:40:54 +03:00
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP65_LAN4 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_LAN3 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP67_LAN4 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP73_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP73_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP73_LAN3 },
|
2008-02-24 08:34:01 +03:00
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP73_LAN4 },
|
2008-04-17 23:12:26 +04:00
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP77_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP77_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP77_LAN3 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP77_LAN4 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_LAN1 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_LAN2 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_LAN3 },
|
|
|
|
{ PCI_VENDOR_NVIDIA, PCI_PRODUCT_NVIDIA_MCP79_LAN4 }
|
2006-03-13 01:40:42 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
int
|
2008-03-26 17:46:21 +03:00
|
|
|
nfe_match(device_t dev, cfdata_t match, void *aux)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
const struct nfe_product *np;
|
|
|
|
int i;
|
|
|
|
|
2009-06-14 10:24:14 +04:00
|
|
|
for (i = 0; i < __arraycount(nfe_devices); i++) {
|
2006-03-13 01:40:42 +03:00
|
|
|
np = &nfe_devices[i];
|
|
|
|
if (PCI_VENDOR(pa->pa_id) == np->vendor &&
|
|
|
|
PCI_PRODUCT(pa->pa_id) == np->product)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-03-26 17:46:21 +03:00
|
|
|
nfe_attach(device_t parent, device_t self, void *aux)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
2008-03-26 17:46:21 +03:00
|
|
|
struct nfe_softc *sc = device_private(self);
|
2006-03-13 01:40:42 +03:00
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
const char *intrstr;
|
|
|
|
struct ifnet *ifp;
|
2010-11-02 19:56:47 +03:00
|
|
|
pcireg_t memtype, csr;
|
2009-02-12 13:33:23 +03:00
|
|
|
int mii_flags = 0;
|
2007-01-01 06:43:04 +03:00
|
|
|
|
2008-03-26 17:46:21 +03:00
|
|
|
sc->sc_dev = self;
|
2010-11-03 17:03:40 +03:00
|
|
|
sc->sc_pc = pa->pa_pc;
|
2012-01-30 23:41:18 +04:00
|
|
|
pci_aprint_devinfo(pa, NULL);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, NFE_PCI_BA);
|
|
|
|
switch (memtype) {
|
|
|
|
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT:
|
|
|
|
case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT:
|
|
|
|
if (pci_mapreg_map(pa, NFE_PCI_BA, memtype, 0, &sc->sc_memt,
|
2010-11-03 17:03:40 +03:00
|
|
|
&sc->sc_memh, NULL, &sc->sc_mems) == 0)
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
default:
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "could not map mem space\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pci_intr_map(pa, &ih) != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "could not map interrupt\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
goto fail;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
intrstr = pci_intr_string(pc, ih);
|
|
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, nfe_intr, sc);
|
|
|
|
if (sc->sc_ih == NULL) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "could not establish interrupt");
|
2006-03-13 01:40:42 +03:00
|
|
|
if (intrstr != NULL)
|
2009-11-26 18:17:08 +03:00
|
|
|
aprint_error(" at %s", intrstr);
|
|
|
|
aprint_error("\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
goto fail;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
|
2010-11-02 19:56:47 +03:00
|
|
|
csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
csr |= PCI_COMMAND_MASTER_ENABLE;
|
|
|
|
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr);
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
sc->sc_flags = 0;
|
|
|
|
|
|
|
|
switch (PCI_PRODUCT(pa->pa_id)) {
|
|
|
|
case PCI_PRODUCT_NVIDIA_NFORCE3_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_NFORCE3_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_NFORCE3_LAN4:
|
|
|
|
case PCI_PRODUCT_NVIDIA_NFORCE3_LAN5:
|
|
|
|
sc->sc_flags |= NFE_JUMBO_SUP | NFE_HW_CSUM;
|
|
|
|
break;
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP51_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP51_LAN2:
|
2008-04-17 23:12:26 +04:00
|
|
|
sc->sc_flags |= NFE_40BIT_ADDR | NFE_PWR_MGMT;
|
|
|
|
break;
|
2006-09-03 11:42:04 +04:00
|
|
|
case PCI_PRODUCT_NVIDIA_MCP61_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP61_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP61_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP61_LAN4:
|
2007-11-14 17:59:50 +03:00
|
|
|
case PCI_PRODUCT_NVIDIA_MCP67_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP67_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP67_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP67_LAN4:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP73_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP73_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP73_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP73_LAN4:
|
2008-04-17 23:12:26 +04:00
|
|
|
sc->sc_flags |= NFE_40BIT_ADDR | NFE_CORRECT_MACADDR |
|
|
|
|
NFE_PWR_MGMT;
|
|
|
|
break;
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP77_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP77_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP77_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP77_LAN4:
|
2009-03-01 11:29:25 +03:00
|
|
|
sc->sc_flags |= NFE_40BIT_ADDR | NFE_HW_CSUM |
|
|
|
|
NFE_CORRECT_MACADDR | NFE_PWR_MGMT;
|
|
|
|
break;
|
2008-04-17 23:12:26 +04:00
|
|
|
case PCI_PRODUCT_NVIDIA_MCP79_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP79_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP79_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP79_LAN4:
|
2009-03-01 11:29:25 +03:00
|
|
|
sc->sc_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM |
|
2008-04-17 23:12:26 +04:00
|
|
|
NFE_CORRECT_MACADDR | NFE_PWR_MGMT;
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
case PCI_PRODUCT_NVIDIA_CK804_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_CK804_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP04_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP04_LAN2:
|
|
|
|
sc->sc_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM;
|
|
|
|
break;
|
2006-09-03 11:42:04 +04:00
|
|
|
case PCI_PRODUCT_NVIDIA_MCP65_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP65_LAN2:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP65_LAN3:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP65_LAN4:
|
2008-04-17 23:12:26 +04:00
|
|
|
sc->sc_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR |
|
|
|
|
NFE_CORRECT_MACADDR | NFE_PWR_MGMT;
|
2009-02-12 13:33:23 +03:00
|
|
|
mii_flags = MIIF_DOPAUSE;
|
2008-04-17 23:12:26 +04:00
|
|
|
break;
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP55_LAN1:
|
|
|
|
case PCI_PRODUCT_NVIDIA_MCP55_LAN2:
|
2006-03-13 01:40:42 +03:00
|
|
|
sc->sc_flags |= NFE_JUMBO_SUP | NFE_40BIT_ADDR | NFE_HW_CSUM |
|
2008-01-26 17:13:06 +03:00
|
|
|
NFE_HW_VLAN | NFE_PWR_MGMT;
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-05-26 02:57:35 +04:00
|
|
|
nfe_poweron(self);
|
2008-01-26 17:13:06 +03:00
|
|
|
|
2008-04-20 12:57:37 +04:00
|
|
|
#ifndef NFE_NO_JUMBO
|
2006-03-13 01:40:42 +03:00
|
|
|
/* enable jumbo frames for adapters that support it */
|
|
|
|
if (sc->sc_flags & NFE_JUMBO_SUP)
|
|
|
|
sc->sc_flags |= NFE_USE_JUMBO;
|
|
|
|
#endif
|
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
/* Check for reversed ethernet address */
|
|
|
|
if ((NFE_READ(sc, NFE_TX_UNK) & NFE_MAC_ADDR_INORDER) != 0)
|
|
|
|
sc->sc_flags |= NFE_CORRECT_MACADDR;
|
|
|
|
|
|
|
|
nfe_get_macaddr(sc, sc->sc_enaddr);
|
|
|
|
aprint_normal_dev(self, "Ethernet address %s\n",
|
|
|
|
ether_sprintf(sc->sc_enaddr));
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
/*
|
|
|
|
* Allocate Tx and Rx rings.
|
|
|
|
*/
|
|
|
|
if (nfe_alloc_tx_ring(sc, &sc->txq) != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "could not allocate Tx ring\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
goto fail;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
2008-06-17 16:59:32 +04:00
|
|
|
mutex_init(&sc->rxq.mtx, MUTEX_DEFAULT, IPL_NET);
|
2008-04-20 12:57:37 +04:00
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
if (nfe_alloc_rx_ring(sc, &sc->rxq) != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "could not allocate Rx ring\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
nfe_free_tx_ring(sc, &sc->txq);
|
2009-03-01 16:34:10 +03:00
|
|
|
goto fail;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
ifp->if_mtu = ETHERMTU;
|
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_ioctl = nfe_ioctl;
|
|
|
|
ifp->if_start = nfe_start;
|
2007-12-09 23:27:42 +03:00
|
|
|
ifp->if_stop = nfe_stop;
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_watchdog = nfe_watchdog;
|
|
|
|
ifp->if_init = nfe_init;
|
|
|
|
ifp->if_baudrate = IF_Gbps(1);
|
|
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, NFE_IFQ_MAXLEN);
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
2008-03-26 17:46:21 +03:00
|
|
|
strlcpy(ifp->if_xname, device_xname(self), IFNAMSIZ);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
if (sc->sc_flags & NFE_USE_JUMBO)
|
*** 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
|
|
|
sc->sc_ethercom.ec_capabilities |= ETHERCAP_JUMBO_MTU;
|
2008-04-17 23:12:26 +04:00
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
#if NVLAN > 0
|
|
|
|
if (sc->sc_flags & NFE_HW_VLAN)
|
|
|
|
sc->sc_ethercom.ec_capabilities |=
|
|
|
|
ETHERCAP_VLAN_HWTAGGING | ETHERCAP_VLAN_MTU;
|
|
|
|
#endif
|
|
|
|
if (sc->sc_flags & NFE_HW_CSUM) {
|
2007-01-09 13:29:27 +03:00
|
|
|
ifp->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;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_mii.mii_ifp = ifp;
|
|
|
|
sc->sc_mii.mii_readreg = nfe_miibus_readreg;
|
|
|
|
sc->sc_mii.mii_writereg = nfe_miibus_writereg;
|
|
|
|
sc->sc_mii.mii_statchg = nfe_miibus_statchg;
|
|
|
|
|
Make many ethernet drivers share the common code for MII media
handling, ether_mediastatus() and ether_mediachange(). Check for
a non-ENXIO error return from mii_mediachg(). (ENXIO indicates
that a PHY is suspended.)
This patch shrinks the source code size by 979 lines. There was
a 5100-byte savings on the NetBSD/i386 kernel configuration, ALL.
I have made a few miscellaneous changes, too:
gem(4): use LIST_EMPTY(), LIST_FOREACH().
mtd(4): handle media ioctls, for a change!
axe(4): do not track link status in sc->axe_link any longer
nfe(4), aue(4), axe(4), udav(4), url(4): do not reset all PHYs
on a change of media
Except for the change to mtd(4), no functional changes are intended.
XXX This patch affects more architectures than I can feasibly
XXX compile and run. I have compiled macppc, sparc64, i386. I
XXX have run the patches on i386 boxen with bnx(4) and sip(4).
XXX Compiling and running on evbmips (MERAKI, ADM5120) is in
XXX progress.
2008-01-20 01:10:14 +03:00
|
|
|
sc->sc_ethercom.ec_mii = &sc->sc_mii;
|
|
|
|
ifmedia_init(&sc->sc_mii.mii_media, 0, ether_mediachange,
|
|
|
|
ether_mediastatus);
|
2009-02-12 13:33:23 +03:00
|
|
|
|
2011-01-23 06:15:06 +03:00
|
|
|
mii_attach(self, &sc->sc_mii, 0xffffffff, MII_PHY_ANY, 0, mii_flags);
|
2009-02-12 13:33:23 +03:00
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(self, "no PHY found!\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL);
|
|
|
|
} else
|
|
|
|
ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO);
|
|
|
|
|
|
|
|
if_attach(ifp);
|
|
|
|
ether_ifattach(ifp, sc->sc_enaddr);
|
*** 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, nfe_ifflags_cb);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-07-10 00:51:58 +04:00
|
|
|
callout_init(&sc->sc_tick_ch, 0);
|
2006-03-13 01:40:42 +03:00
|
|
|
callout_setfunc(&sc->sc_tick_ch, nfe_tick, sc);
|
|
|
|
|
2009-09-05 18:09:55 +04:00
|
|
|
if (pmf_device_register(self, NULL, nfe_resume))
|
2007-12-09 23:27:42 +03:00
|
|
|
pmf_class_network_register(self, ifp);
|
2009-09-05 18:09:55 +04:00
|
|
|
else
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (sc->sc_ih != NULL) {
|
|
|
|
pci_intr_disestablish(pc, sc->sc_ih);
|
|
|
|
sc->sc_ih = NULL;
|
|
|
|
}
|
2010-11-03 17:03:40 +03:00
|
|
|
if (sc->sc_mems != 0) {
|
|
|
|
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
|
|
|
|
sc->sc_mems = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_detach(device_t self, int flags)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = device_private(self);
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
nfe_stop(ifp, 1);
|
|
|
|
|
|
|
|
pmf_device_deregister(self);
|
|
|
|
callout_destroy(&sc->sc_tick_ch);
|
|
|
|
ether_ifdetach(ifp);
|
|
|
|
if_detach(ifp);
|
|
|
|
mii_detach(&sc->sc_mii, MII_PHY_ANY, MII_OFFSET_ANY);
|
|
|
|
|
|
|
|
nfe_free_rx_ring(sc, &sc->rxq);
|
|
|
|
mutex_destroy(&sc->rxq.mtx);
|
|
|
|
nfe_free_tx_ring(sc, &sc->txq);
|
|
|
|
|
|
|
|
if (sc->sc_ih != NULL) {
|
|
|
|
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
|
|
|
|
sc->sc_ih = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((sc->sc_flags & NFE_CORRECT_MACADDR) != 0) {
|
|
|
|
nfe_set_macaddr(sc, sc->sc_enaddr);
|
|
|
|
} else {
|
|
|
|
NFE_WRITE(sc, NFE_MACADDR_LO,
|
|
|
|
sc->sc_enaddr[0] << 8 | sc->sc_enaddr[1]);
|
|
|
|
NFE_WRITE(sc, NFE_MACADDR_HI,
|
|
|
|
sc->sc_enaddr[2] << 24 | sc->sc_enaddr[3] << 16 |
|
|
|
|
sc->sc_enaddr[4] << 8 | sc->sc_enaddr[5]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_mems != 0) {
|
|
|
|
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_mems);
|
|
|
|
sc->sc_mems = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
return 0;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-03-26 17:46:21 +03:00
|
|
|
nfe_miibus_statchg(device_t dev)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
2008-03-26 17:46:21 +03:00
|
|
|
struct nfe_softc *sc = device_private(dev);
|
2006-03-13 01:40:42 +03:00
|
|
|
struct mii_data *mii = &sc->sc_mii;
|
|
|
|
uint32_t phy, seed, misc = NFE_MISC1_MAGIC, link = NFE_MEDIA_SET;
|
|
|
|
|
|
|
|
phy = NFE_READ(sc, NFE_PHY_IFACE);
|
|
|
|
phy &= ~(NFE_PHY_HDX | NFE_PHY_100TX | NFE_PHY_1000T);
|
|
|
|
|
|
|
|
seed = NFE_READ(sc, NFE_RNDSEED);
|
|
|
|
seed &= ~NFE_SEED_MASK;
|
|
|
|
|
|
|
|
if ((mii->mii_media_active & IFM_GMASK) == IFM_HDX) {
|
|
|
|
phy |= NFE_PHY_HDX; /* half-duplex */
|
|
|
|
misc |= NFE_MISC1_HDX;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (IFM_SUBTYPE(mii->mii_media_active)) {
|
|
|
|
case IFM_1000_T: /* full-duplex only */
|
|
|
|
link |= NFE_MEDIA_1000T;
|
|
|
|
seed |= NFE_SEED_1000T;
|
|
|
|
phy |= NFE_PHY_1000T;
|
|
|
|
break;
|
|
|
|
case IFM_100_TX:
|
|
|
|
link |= NFE_MEDIA_100TX;
|
|
|
|
seed |= NFE_SEED_100TX;
|
|
|
|
phy |= NFE_PHY_100TX;
|
|
|
|
break;
|
|
|
|
case IFM_10_T:
|
|
|
|
link |= NFE_MEDIA_10T;
|
|
|
|
seed |= NFE_SEED_10T;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_RNDSEED, seed); /* XXX: gigabit NICs only? */
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_IFACE, phy);
|
|
|
|
NFE_WRITE(sc, NFE_MISC1, misc);
|
|
|
|
NFE_WRITE(sc, NFE_LINKSPEED, link);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2008-03-26 17:46:21 +03:00
|
|
|
nfe_miibus_readreg(device_t dev, int phy, int reg)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
2008-03-26 17:46:21 +03:00
|
|
|
struct nfe_softc *sc = device_private(dev);
|
2006-03-13 01:40:42 +03:00
|
|
|
uint32_t val;
|
|
|
|
int ntries;
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_STATUS, 0xf);
|
|
|
|
|
|
|
|
if (NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY) {
|
|
|
|
NFE_WRITE(sc, NFE_PHY_CTL, NFE_PHY_BUSY);
|
|
|
|
DELAY(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_CTL, (phy << NFE_PHYADD_SHIFT) | reg);
|
|
|
|
|
|
|
|
for (ntries = 0; ntries < 1000; ntries++) {
|
|
|
|
DELAY(100);
|
|
|
|
if (!(NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ntries == 1000) {
|
|
|
|
DPRINTFN(2, ("%s: timeout waiting for PHY\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2006-03-13 01:40:42 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NFE_READ(sc, NFE_PHY_STATUS) & NFE_PHY_ERROR) {
|
|
|
|
DPRINTFN(2, ("%s: could not read PHY\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2006-03-13 01:40:42 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = NFE_READ(sc, NFE_PHY_DATA);
|
|
|
|
if (val != 0xffffffff && val != 0)
|
|
|
|
sc->mii_phyaddr = phy;
|
|
|
|
|
|
|
|
DPRINTFN(2, ("%s: mii read phy %d reg 0x%x ret 0x%x\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev), phy, reg, val));
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-03-26 17:46:21 +03:00
|
|
|
nfe_miibus_writereg(device_t dev, int phy, int reg, int val)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
2008-03-26 17:46:21 +03:00
|
|
|
struct nfe_softc *sc = device_private(dev);
|
2006-03-13 01:40:42 +03:00
|
|
|
uint32_t ctl;
|
|
|
|
int ntries;
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_STATUS, 0xf);
|
|
|
|
|
|
|
|
if (NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY) {
|
|
|
|
NFE_WRITE(sc, NFE_PHY_CTL, NFE_PHY_BUSY);
|
|
|
|
DELAY(100);
|
|
|
|
}
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_DATA, val);
|
|
|
|
ctl = NFE_PHY_WRITE | (phy << NFE_PHYADD_SHIFT) | reg;
|
|
|
|
NFE_WRITE(sc, NFE_PHY_CTL, ctl);
|
|
|
|
|
|
|
|
for (ntries = 0; ntries < 1000; ntries++) {
|
|
|
|
DELAY(100);
|
|
|
|
if (!(NFE_READ(sc, NFE_PHY_CTL) & NFE_PHY_BUSY))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#ifdef NFE_DEBUG
|
|
|
|
if (nfedebug >= 2 && ntries == 1000)
|
|
|
|
printf("could not write to PHY\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_intr(void *arg)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = arg;
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
uint32_t r;
|
2007-02-28 20:40:11 +03:00
|
|
|
int handled;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((ifp->if_flags & IFF_UP) == 0)
|
|
|
|
return 0;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
handled = 0;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
for (;;) {
|
|
|
|
r = NFE_READ(sc, NFE_IRQ_STATUS);
|
|
|
|
if ((r & NFE_IRQ_WANTED) == 0)
|
|
|
|
break;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
NFE_WRITE(sc, NFE_IRQ_STATUS, r);
|
|
|
|
handled = 1;
|
|
|
|
DPRINTFN(5, ("nfe_intr: interrupt register %x\n", r));
|
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
if ((r & (NFE_IRQ_RXERR|NFE_IRQ_RX_NOBUF|NFE_IRQ_RX)) != 0) {
|
2007-02-28 20:40:11 +03:00
|
|
|
/* check Rx ring */
|
|
|
|
nfe_rxeof(sc);
|
|
|
|
}
|
2008-04-17 23:12:26 +04:00
|
|
|
if ((r & (NFE_IRQ_TXERR|NFE_IRQ_TXERR2|NFE_IRQ_TX_DONE)) != 0) {
|
2007-02-28 20:40:11 +03:00
|
|
|
/* check Tx ring */
|
|
|
|
nfe_txeof(sc);
|
|
|
|
}
|
|
|
|
if ((r & NFE_IRQ_LINK) != 0) {
|
|
|
|
NFE_READ(sc, NFE_PHY_STATUS);
|
|
|
|
NFE_WRITE(sc, NFE_PHY_STATUS, 0xf);
|
|
|
|
DPRINTF(("%s: link state changed\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2007-02-28 20:40:11 +03:00
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if (handled && !IF_IS_EMPTY(&ifp->if_snd))
|
2007-01-05 04:33:57 +03:00
|
|
|
nfe_start(ifp);
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
return handled;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
*** 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
|
|
|
|
nfe_ifflags_cb(struct ethercom *ec)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = &ec->ec_if;
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
|
|
|
int change = ifp->if_flags ^ sc->sc_if_flags;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If only the PROMISC flag changes, then
|
|
|
|
* don't do a full re-init of the chip, just update
|
|
|
|
* the Rx filter.
|
|
|
|
*/
|
|
|
|
if ((change & ~(IFF_CANTCHANGE|IFF_DEBUG)) != 0)
|
|
|
|
return ENETRESET;
|
|
|
|
else if ((change & IFF_PROMISC) != 0)
|
|
|
|
nfe_setmulti(sc);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
int
|
2007-03-04 08:59:00 +03:00
|
|
|
nfe_ioctl(struct ifnet *ifp, u_long cmd, void *data)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
|
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
|
|
int s, error = 0;
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
switch (cmd) {
|
*** 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
|
|
|
case SIOCINITIFADDR:
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_flags |= IFF_UP;
|
|
|
|
nfe_init(ifp);
|
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
arp_ifinit(ifp, ifa);
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2008-02-07 04:21:52 +03:00
|
|
|
if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
|
|
|
|
break;
|
2008-04-17 23:12:26 +04:00
|
|
|
|
2008-02-07 04:21:52 +03:00
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
|
|
|
|
;
|
|
|
|
else if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
nfe_setmulti(sc);
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
}
|
*** 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
|
|
|
sc->sc_if_flags = ifp->if_flags;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_txdesc32_sync(struct nfe_softc *sc, struct nfe_desc32 *desc32, int ops)
|
|
|
|
{
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)desc32 - (char *)sc->txq.desc32,
|
2006-03-13 01:40:42 +03:00
|
|
|
sizeof (struct nfe_desc32), ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_txdesc64_sync(struct nfe_softc *sc, struct nfe_desc64 *desc64, int ops)
|
|
|
|
{
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)desc64 - (char *)sc->txq.desc64,
|
2006-03-13 01:40:42 +03:00
|
|
|
sizeof (struct nfe_desc64), ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_txdesc32_rsync(struct nfe_softc *sc, int start, int end, int ops)
|
|
|
|
{
|
|
|
|
if (end > start) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc32[start] - (char *)sc->txq.desc32,
|
|
|
|
(char *)&sc->txq.desc32[end] -
|
|
|
|
(char *)&sc->txq.desc32[start], ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* sync from 'start' to end of ring */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc32[start] - (char *)sc->txq.desc32,
|
|
|
|
(char *)&sc->txq.desc32[NFE_TX_RING_COUNT] -
|
|
|
|
(char *)&sc->txq.desc32[start], ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
/* sync from start of ring to 'end' */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map, 0,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc32[end] - (char *)sc->txq.desc32, ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_txdesc64_rsync(struct nfe_softc *sc, int start, int end, int ops)
|
|
|
|
{
|
|
|
|
if (end > start) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc64[start] - (char *)sc->txq.desc64,
|
|
|
|
(char *)&sc->txq.desc64[end] -
|
|
|
|
(char *)&sc->txq.desc64[start], ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* sync from 'start' to end of ring */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc64[start] - (char *)sc->txq.desc64,
|
|
|
|
(char *)&sc->txq.desc64[NFE_TX_RING_COUNT] -
|
|
|
|
(char *)&sc->txq.desc64[start], ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
/* sync from start of ring to 'end' */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->txq.map, 0,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)&sc->txq.desc64[end] - (char *)sc->txq.desc64, ops);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_rxdesc32_sync(struct nfe_softc *sc, struct nfe_desc32 *desc32, int ops)
|
|
|
|
{
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->rxq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)desc32 - (char *)sc->rxq.desc32,
|
2006-03-13 01:40:42 +03:00
|
|
|
sizeof (struct nfe_desc32), ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_rxdesc64_sync(struct nfe_softc *sc, struct nfe_desc64 *desc64, int ops)
|
|
|
|
{
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->rxq.map,
|
2007-03-04 08:59:00 +03:00
|
|
|
(char *)desc64 - (char *)sc->rxq.desc64,
|
2006-03-13 01:40:42 +03:00
|
|
|
sizeof (struct nfe_desc64), ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_rxeof(struct nfe_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
struct nfe_desc32 *desc32;
|
|
|
|
struct nfe_desc64 *desc64;
|
|
|
|
struct nfe_rx_data *data;
|
|
|
|
struct nfe_jbuf *jbuf;
|
|
|
|
struct mbuf *m, *mnew;
|
|
|
|
bus_addr_t physaddr;
|
|
|
|
uint16_t flags;
|
2007-02-28 20:40:11 +03:00
|
|
|
int error, len, i;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
desc32 = NULL;
|
|
|
|
desc64 = NULL;
|
2007-02-28 20:40:11 +03:00
|
|
|
for (i = sc->rxq.cur;; i = NFE_RX_NEXTDESC(i)) {
|
|
|
|
data = &sc->rxq.data[i];
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
2007-02-28 20:40:11 +03:00
|
|
|
desc64 = &sc->rxq.desc64[i];
|
|
|
|
nfe_rxdesc64_sync(sc, desc64,
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
flags = le16toh(desc64->flags);
|
|
|
|
len = le16toh(desc64->length) & 0x3fff;
|
|
|
|
} else {
|
2007-02-28 20:40:11 +03:00
|
|
|
desc32 = &sc->rxq.desc32[i];
|
|
|
|
nfe_rxdesc32_sync(sc, desc32,
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
flags = le16toh(desc32->flags);
|
|
|
|
len = le16toh(desc32->length) & 0x3fff;
|
|
|
|
}
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_RX_READY) != 0)
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
|
|
|
|
if ((sc->sc_flags & (NFE_JUMBO_SUP | NFE_40BIT_ADDR)) == 0) {
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_RX_VALID_V1) == 0)
|
2006-03-13 01:40:42 +03:00
|
|
|
goto skip;
|
|
|
|
|
|
|
|
if ((flags & NFE_RX_FIXME_V1) == NFE_RX_FIXME_V1) {
|
|
|
|
flags &= ~NFE_RX_ERROR;
|
|
|
|
len--; /* fix buffer length */
|
|
|
|
}
|
|
|
|
} else {
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_RX_VALID_V2) == 0)
|
2006-03-13 01:40:42 +03:00
|
|
|
goto skip;
|
|
|
|
|
|
|
|
if ((flags & NFE_RX_FIXME_V2) == NFE_RX_FIXME_V2) {
|
|
|
|
flags &= ~NFE_RX_ERROR;
|
|
|
|
len--; /* fix buffer length */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & NFE_RX_ERROR) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to allocate a new mbuf for this ring element and load
|
|
|
|
* it before processing the current mbuf. If the ring element
|
|
|
|
* cannot be loaded, drop the received packet and reuse the
|
|
|
|
* old mbuf. In the unlikely case that the old mbuf can't be
|
|
|
|
* reloaded either, explicitly panic.
|
|
|
|
*/
|
|
|
|
MGETHDR(mnew, M_DONTWAIT, MT_DATA);
|
|
|
|
if (mnew == NULL) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_USE_JUMBO) {
|
2007-09-24 17:17:53 +04:00
|
|
|
physaddr =
|
|
|
|
sc->rxq.jbuf[sc->rxq.jbufmap[i]].physaddr;
|
|
|
|
if ((jbuf = nfe_jalloc(sc, i)) == NULL) {
|
|
|
|
if (len > MCLBYTES) {
|
|
|
|
m_freem(mnew);
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip1;
|
|
|
|
}
|
|
|
|
MCLGET(mnew, M_DONTWAIT);
|
|
|
|
if ((mnew->m_flags & M_EXT) == 0) {
|
|
|
|
m_freem(mnew);
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip1;
|
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
(void)memcpy(mtod(mnew, void *),
|
2007-09-24 17:17:53 +04:00
|
|
|
mtod(data->m, const void *), len);
|
|
|
|
m = mnew;
|
|
|
|
goto mbufcopied;
|
|
|
|
} else {
|
|
|
|
MEXTADD(mnew, jbuf->buf, NFE_JBYTES, 0, nfe_jfree, sc);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->rxq.jmap,
|
|
|
|
mtod(data->m, char *) - (char *)sc->rxq.jpool,
|
|
|
|
NFE_JBYTES, BUS_DMASYNC_POSTREAD);
|
|
|
|
|
|
|
|
physaddr = jbuf->physaddr;
|
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
} else {
|
|
|
|
MCLGET(mnew, M_DONTWAIT);
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((mnew->m_flags & M_EXT) == 0) {
|
2006-03-13 01:40:42 +03:00
|
|
|
m_freem(mnew);
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->map);
|
|
|
|
|
2007-09-24 17:17:53 +04:00
|
|
|
error = bus_dmamap_load(sc->sc_dmat, data->map,
|
|
|
|
mtod(mnew, void *), MCLBYTES, NULL,
|
|
|
|
BUS_DMA_READ | BUS_DMA_NOWAIT);
|
2006-03-13 01:40:42 +03:00
|
|
|
if (error != 0) {
|
|
|
|
m_freem(mnew);
|
|
|
|
|
|
|
|
/* try to reload the old mbuf */
|
2007-09-24 17:17:53 +04:00
|
|
|
error = bus_dmamap_load(sc->sc_dmat, data->map,
|
|
|
|
mtod(data->m, void *), MCLBYTES, NULL,
|
2006-03-13 01:40:42 +03:00
|
|
|
BUS_DMA_READ | BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
|
|
|
/* very unlikely that it will fail.. */
|
|
|
|
panic("%s: could not load old rx mbuf",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev));
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
physaddr = data->map->dm_segs[0].ds_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* New mbuf successfully loaded, update Rx ring and continue
|
|
|
|
* processing.
|
|
|
|
*/
|
|
|
|
m = data->m;
|
|
|
|
data->m = mnew;
|
|
|
|
|
2007-09-24 17:17:53 +04:00
|
|
|
mbufcopied:
|
2006-03-13 01:40:42 +03:00
|
|
|
/* finalize mbuf */
|
|
|
|
m->m_pkthdr.len = m->m_len = len;
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
|
2007-01-09 13:29:27 +03:00
|
|
|
if ((sc->sc_flags & NFE_HW_CSUM) != 0) {
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* no way to check M_CSUM_IPv4_BAD or non-IPv4 packets?
|
|
|
|
*/
|
|
|
|
if (flags & NFE_RX_IP_CSUMOK) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_IPv4;
|
|
|
|
DPRINTFN(3, ("%s: ip4csum-rx ok\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2007-01-09 13:29:27 +03:00
|
|
|
}
|
|
|
|
/*
|
|
|
|
* XXX
|
|
|
|
* no way to check M_CSUM_TCP_UDP_BAD or
|
|
|
|
* other protocols?
|
|
|
|
*/
|
|
|
|
if (flags & NFE_RX_UDP_CSUMOK) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_UDPv4;
|
|
|
|
DPRINTFN(3, ("%s: udp4csum-rx ok\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2007-01-09 13:29:27 +03:00
|
|
|
} else if (flags & NFE_RX_TCP_CSUMOK) {
|
|
|
|
m->m_pkthdr.csum_flags |= M_CSUM_TCPv4;
|
|
|
|
DPRINTFN(3, ("%s: tcp4csum-rx ok\n",
|
2008-03-26 17:46:21 +03:00
|
|
|
device_xname(sc->sc_dev)));
|
2007-01-09 13:29:27 +03:00
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m);
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_ipackets++;
|
|
|
|
(*ifp->if_input)(ifp, m);
|
|
|
|
|
2007-09-24 17:17:53 +04:00
|
|
|
skip1:
|
2006-03-13 01:40:42 +03:00
|
|
|
/* update mapping address in h/w descriptor */
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
#if defined(__LP64__)
|
|
|
|
desc64->physaddr[0] = htole32(physaddr >> 32);
|
|
|
|
#endif
|
|
|
|
desc64->physaddr[1] = htole32(physaddr & 0xffffffff);
|
|
|
|
} else {
|
|
|
|
desc32->physaddr = htole32(physaddr);
|
|
|
|
}
|
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
skip:
|
2007-02-28 20:40:11 +03:00
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
2006-03-13 01:40:42 +03:00
|
|
|
desc64->length = htole16(sc->rxq.bufsz);
|
|
|
|
desc64->flags = htole16(NFE_RX_READY);
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
nfe_rxdesc64_sync(sc, desc64,
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
} else {
|
|
|
|
desc32->length = htole16(sc->rxq.bufsz);
|
|
|
|
desc32->flags = htole16(NFE_RX_READY);
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
nfe_rxdesc32_sync(sc, desc32,
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
}
|
2007-02-28 20:40:11 +03:00
|
|
|
/* update current RX pointer */
|
|
|
|
sc->rxq.cur = i;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_txeof(struct nfe_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_ethercom.ec_if;
|
|
|
|
struct nfe_desc32 *desc32;
|
|
|
|
struct nfe_desc64 *desc64;
|
|
|
|
struct nfe_tx_data *data = NULL;
|
2007-02-28 20:40:11 +03:00
|
|
|
int i;
|
2006-03-13 01:40:42 +03:00
|
|
|
uint16_t flags;
|
2008-04-17 23:12:26 +04:00
|
|
|
char buf[128];
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
for (i = sc->txq.next;
|
|
|
|
sc->txq.queued > 0;
|
|
|
|
i = NFE_TX_NEXTDESC(i), sc->txq.queued--) {
|
2006-03-13 01:40:42 +03:00
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
2007-02-28 20:40:11 +03:00
|
|
|
desc64 = &sc->txq.desc64[i];
|
|
|
|
nfe_txdesc64_sync(sc, desc64,
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
flags = le16toh(desc64->flags);
|
|
|
|
} else {
|
2007-02-28 20:40:11 +03:00
|
|
|
desc32 = &sc->txq.desc32[i];
|
|
|
|
nfe_txdesc32_sync(sc, desc32,
|
|
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
flags = le16toh(desc32->flags);
|
|
|
|
}
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_TX_VALID) != 0)
|
2006-03-13 01:40:42 +03:00
|
|
|
break;
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
data = &sc->txq.data[i];
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
if ((sc->sc_flags & (NFE_JUMBO_SUP | NFE_40BIT_ADDR)) == 0) {
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_TX_LASTFRAG_V1) == 0 &&
|
|
|
|
data->m == NULL)
|
|
|
|
continue;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
if ((flags & NFE_TX_ERROR_V1) != 0) {
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(buf, sizeof(buf), NFE_V1_TXERR, flags);
|
2008-04-18 00:16:46 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "tx v1 error %s\n",
|
2008-12-17 01:35:21 +03:00
|
|
|
buf);
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_oerrors++;
|
|
|
|
} else
|
|
|
|
ifp->if_opackets++;
|
|
|
|
} else {
|
2007-02-28 20:40:11 +03:00
|
|
|
if ((flags & NFE_TX_LASTFRAG_V2) == 0 &&
|
|
|
|
data->m == NULL)
|
|
|
|
continue;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
if ((flags & NFE_TX_ERROR_V2) != 0) {
|
2008-12-17 01:35:21 +03:00
|
|
|
snprintb(buf, sizeof(buf), NFE_V2_TXERR, flags);
|
2008-04-17 23:50:38 +04:00
|
|
|
aprint_error_dev(sc->sc_dev, "tx v2 error %s\n",
|
2008-12-17 01:35:21 +03:00
|
|
|
buf);
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_oerrors++;
|
|
|
|
} else
|
|
|
|
ifp->if_opackets++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data->m == NULL) { /* should not get there */
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"last fragment bit w/o associated mbuf!\n");
|
2007-02-28 20:40:11 +03:00
|
|
|
continue;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* last fragment of the mbuf chain transmitted */
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->active, 0,
|
|
|
|
data->active->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->active);
|
|
|
|
m_freem(data->m);
|
|
|
|
data->m = NULL;
|
2007-02-28 20:40:11 +03:00
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
sc->txq.next = i;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if (sc->txq.queued < NFE_TX_RING_COUNT) {
|
|
|
|
/* at least one slot freed */
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if (sc->txq.queued == 0) {
|
|
|
|
/* all queued packets are sent */
|
|
|
|
ifp->if_timer = 0;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_encap(struct nfe_softc *sc, struct mbuf *m0)
|
|
|
|
{
|
|
|
|
struct nfe_desc32 *desc32;
|
|
|
|
struct nfe_desc64 *desc64;
|
|
|
|
struct nfe_tx_data *data;
|
|
|
|
bus_dmamap_t map;
|
2007-01-09 13:29:27 +03:00
|
|
|
uint16_t flags, csumflags;
|
2006-03-13 01:40:42 +03:00
|
|
|
#if NVLAN > 0
|
|
|
|
struct m_tag *mtag;
|
|
|
|
uint32_t vtag = 0;
|
|
|
|
#endif
|
2007-01-01 07:13:25 +03:00
|
|
|
int error, i, first;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
desc32 = NULL;
|
|
|
|
desc64 = NULL;
|
|
|
|
data = NULL;
|
2007-01-01 07:13:25 +03:00
|
|
|
|
|
|
|
flags = 0;
|
2007-01-09 13:29:27 +03:00
|
|
|
csumflags = 0;
|
2007-01-01 07:13:25 +03:00
|
|
|
first = sc->txq.cur;
|
|
|
|
|
|
|
|
map = sc->txq.data[first].map;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
error = bus_dmamap_load_mbuf(sc->sc_dmat, map, m0, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not map mbuf (error %d)\n",
|
|
|
|
error);
|
2006-03-13 01:40:42 +03:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->txq.queued + map->dm_nsegs >= NFE_TX_RING_COUNT - 1) {
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, map);
|
|
|
|
return ENOBUFS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if NVLAN > 0
|
|
|
|
/* setup h/w VLAN tagging */
|
2006-12-27 21:36:09 +03:00
|
|
|
if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_ethercom, m0)) != NULL)
|
2006-03-13 01:40:42 +03:00
|
|
|
vtag = NFE_TX_VTAG | VLAN_TAG_VALUE(mtag);
|
|
|
|
#endif
|
2007-01-09 13:29:27 +03:00
|
|
|
if ((sc->sc_flags & NFE_HW_CSUM) != 0) {
|
|
|
|
if (m0->m_pkthdr.csum_flags & M_CSUM_IPv4)
|
|
|
|
csumflags |= NFE_TX_IP_CSUM;
|
|
|
|
if (m0->m_pkthdr.csum_flags & (M_CSUM_TCPv4 | M_CSUM_UDPv4))
|
2007-02-28 20:40:11 +03:00
|
|
|
csumflags |= NFE_TX_TCP_UDP_CSUM;
|
2007-01-09 13:29:27 +03:00
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
for (i = 0; i < map->dm_nsegs; i++) {
|
|
|
|
data = &sc->txq.data[sc->txq.cur];
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc64 = &sc->txq.desc64[sc->txq.cur];
|
|
|
|
#if defined(__LP64__)
|
|
|
|
desc64->physaddr[0] =
|
|
|
|
htole32(map->dm_segs[i].ds_addr >> 32);
|
|
|
|
#endif
|
|
|
|
desc64->physaddr[1] =
|
|
|
|
htole32(map->dm_segs[i].ds_addr & 0xffffffff);
|
|
|
|
desc64->length = htole16(map->dm_segs[i].ds_len - 1);
|
|
|
|
desc64->flags = htole16(flags);
|
2007-01-09 13:29:27 +03:00
|
|
|
desc64->vtag = 0;
|
2006-03-13 01:40:42 +03:00
|
|
|
} else {
|
|
|
|
desc32 = &sc->txq.desc32[sc->txq.cur];
|
|
|
|
|
|
|
|
desc32->physaddr = htole32(map->dm_segs[i].ds_addr);
|
|
|
|
desc32->length = htole16(map->dm_segs[i].ds_len - 1);
|
|
|
|
desc32->flags = htole16(flags);
|
|
|
|
}
|
|
|
|
|
2007-01-09 13:29:27 +03:00
|
|
|
/*
|
|
|
|
* Setting of the valid bit in the first descriptor is
|
|
|
|
* deferred until the whole chain is fully setup.
|
|
|
|
*/
|
|
|
|
flags |= NFE_TX_VALID;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
sc->txq.queued++;
|
2007-02-28 20:40:11 +03:00
|
|
|
sc->txq.cur = NFE_TX_NEXTDESC(sc->txq.cur);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
2007-01-01 07:13:25 +03:00
|
|
|
/* the whole mbuf chain has been setup */
|
2006-03-13 01:40:42 +03:00
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
2007-01-01 07:13:25 +03:00
|
|
|
/* fix last descriptor */
|
2006-03-13 01:40:42 +03:00
|
|
|
flags |= NFE_TX_LASTFRAG_V2;
|
|
|
|
desc64->flags = htole16(flags);
|
2007-01-01 07:13:25 +03:00
|
|
|
|
2007-01-09 13:29:27 +03:00
|
|
|
/* Checksum flags and vtag belong to the first fragment only. */
|
|
|
|
#if NVLAN > 0
|
|
|
|
sc->txq.desc64[first].vtag = htole32(vtag);
|
|
|
|
#endif
|
|
|
|
sc->txq.desc64[first].flags |= htole16(csumflags);
|
|
|
|
|
2007-01-01 07:13:25 +03:00
|
|
|
/* finally, set the valid bit in the first descriptor */
|
|
|
|
sc->txq.desc64[first].flags |= htole16(NFE_TX_VALID);
|
2006-03-13 01:40:42 +03:00
|
|
|
} else {
|
2007-01-01 07:13:25 +03:00
|
|
|
/* fix last descriptor */
|
2006-03-13 01:40:42 +03:00
|
|
|
if (sc->sc_flags & NFE_JUMBO_SUP)
|
|
|
|
flags |= NFE_TX_LASTFRAG_V2;
|
|
|
|
else
|
|
|
|
flags |= NFE_TX_LASTFRAG_V1;
|
|
|
|
desc32->flags = htole16(flags);
|
2007-01-01 07:13:25 +03:00
|
|
|
|
2007-01-09 13:29:27 +03:00
|
|
|
/* Checksum flags belong to the first fragment only. */
|
|
|
|
sc->txq.desc32[first].flags |= htole16(csumflags);
|
|
|
|
|
2007-01-01 07:13:25 +03:00
|
|
|
/* finally, set the valid bit in the first descriptor */
|
|
|
|
sc->txq.desc32[first].flags |= htole16(NFE_TX_VALID);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
data->m = m0;
|
|
|
|
data->active = map;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, map, 0, map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_start(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
2007-02-28 20:40:11 +03:00
|
|
|
int old = sc->txq.queued;
|
2006-03-13 01:40:42 +03:00
|
|
|
struct mbuf *m0;
|
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
|
2007-09-24 17:11:08 +04:00
|
|
|
return;
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
for (;;) {
|
|
|
|
IFQ_POLL(&ifp->if_snd, m0);
|
|
|
|
if (m0 == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (nfe_encap(sc, m0) != 0) {
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* packet put in h/w queue, remove from s/w queue */
|
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m0);
|
|
|
|
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m0);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
if (sc->txq.queued != old) {
|
|
|
|
/* packets are queued */
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR)
|
|
|
|
nfe_txdesc64_rsync(sc, old, sc->txq.cur,
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
else
|
|
|
|
nfe_txdesc32_rsync(sc, old, sc->txq.cur,
|
|
|
|
BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
|
|
|
|
/* kick Tx */
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_KICKTX | sc->rxtxctl);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-02-28 20:40:11 +03:00
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
|
|
|
ifp->if_timer = 5;
|
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_watchdog(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
|
|
|
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev, "watchdog timeout\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
ifp->if_flags &= ~IFF_RUNNING;
|
|
|
|
nfe_init(ifp);
|
|
|
|
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_init(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
|
|
|
uint32_t tmp;
|
Make many ethernet drivers share the common code for MII media
handling, ether_mediastatus() and ether_mediachange(). Check for
a non-ENXIO error return from mii_mediachg(). (ENXIO indicates
that a PHY is suspended.)
This patch shrinks the source code size by 979 lines. There was
a 5100-byte savings on the NetBSD/i386 kernel configuration, ALL.
I have made a few miscellaneous changes, too:
gem(4): use LIST_EMPTY(), LIST_FOREACH().
mtd(4): handle media ioctls, for a change!
axe(4): do not track link status in sc->axe_link any longer
nfe(4), aue(4), axe(4), udav(4), url(4): do not reset all PHYs
on a change of media
Except for the change to mtd(4), no functional changes are intended.
XXX This patch affects more architectures than I can feasibly
XXX compile and run. I have compiled macppc, sparc64, i386. I
XXX have run the patches on i386 boxen with bnx(4) and sip(4).
XXX Compiling and running on evbmips (MERAKI, ADM5120) is in
XXX progress.
2008-01-20 01:10:14 +03:00
|
|
|
int rc = 0, s;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
nfe_stop(ifp, 0);
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_TX_UNK, 0);
|
|
|
|
NFE_WRITE(sc, NFE_STATUS, 0);
|
|
|
|
|
|
|
|
sc->rxtxctl = NFE_RXTX_BIT2;
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR)
|
|
|
|
sc->rxtxctl |= NFE_RXTX_V3MAGIC;
|
|
|
|
else if (sc->sc_flags & NFE_JUMBO_SUP)
|
|
|
|
sc->rxtxctl |= NFE_RXTX_V2MAGIC;
|
|
|
|
if (sc->sc_flags & NFE_HW_CSUM)
|
|
|
|
sc->rxtxctl |= NFE_RXTX_RXCSUM;
|
|
|
|
#if NVLAN > 0
|
|
|
|
/*
|
|
|
|
* Although the adapter is capable of stripping VLAN tags from received
|
|
|
|
* frames (NFE_RXTX_VTAG_STRIP), we do not enable this functionality on
|
|
|
|
* purpose. This will be done in software by our network stack.
|
|
|
|
*/
|
|
|
|
if (sc->sc_flags & NFE_HW_VLAN)
|
|
|
|
sc->rxtxctl |= NFE_RXTX_VTAG_INSERT;
|
|
|
|
#endif
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_RESET | sc->rxtxctl);
|
|
|
|
DELAY(10);
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, sc->rxtxctl);
|
|
|
|
|
|
|
|
#if NVLAN
|
|
|
|
if (sc->sc_flags & NFE_HW_VLAN)
|
|
|
|
NFE_WRITE(sc, NFE_VTAG_CTL, NFE_VTAG_ENABLE);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_SETUP_R6, 0);
|
|
|
|
|
|
|
|
/* set MAC address */
|
|
|
|
nfe_set_macaddr(sc, sc->sc_enaddr);
|
|
|
|
|
|
|
|
/* tell MAC where rings are in memory */
|
|
|
|
#ifdef __LP64__
|
|
|
|
NFE_WRITE(sc, NFE_RX_RING_ADDR_HI, sc->rxq.physaddr >> 32);
|
|
|
|
#endif
|
|
|
|
NFE_WRITE(sc, NFE_RX_RING_ADDR_LO, sc->rxq.physaddr & 0xffffffff);
|
|
|
|
#ifdef __LP64__
|
|
|
|
NFE_WRITE(sc, NFE_TX_RING_ADDR_HI, sc->txq.physaddr >> 32);
|
|
|
|
#endif
|
|
|
|
NFE_WRITE(sc, NFE_TX_RING_ADDR_LO, sc->txq.physaddr & 0xffffffff);
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_RING_SIZE,
|
|
|
|
(NFE_RX_RING_COUNT - 1) << 16 |
|
|
|
|
(NFE_TX_RING_COUNT - 1));
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_RXBUFSZ, sc->rxq.bufsz);
|
|
|
|
|
|
|
|
/* force MAC to wakeup */
|
|
|
|
tmp = NFE_READ(sc, NFE_PWR_STATE);
|
|
|
|
NFE_WRITE(sc, NFE_PWR_STATE, tmp | NFE_PWR_WAKEUP);
|
|
|
|
DELAY(10);
|
|
|
|
tmp = NFE_READ(sc, NFE_PWR_STATE);
|
|
|
|
NFE_WRITE(sc, NFE_PWR_STATE, tmp | NFE_PWR_VALID);
|
|
|
|
|
2007-01-05 04:33:57 +03:00
|
|
|
s = splnet();
|
2009-01-18 14:21:06 +03:00
|
|
|
NFE_WRITE(sc, NFE_IRQ_MASK, 0);
|
2007-01-05 04:33:57 +03:00
|
|
|
nfe_intr(sc); /* XXX clear IRQ status registers */
|
2009-01-18 14:21:06 +03:00
|
|
|
NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_WANTED);
|
2007-01-05 04:33:57 +03:00
|
|
|
splx(s);
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
#if 1
|
|
|
|
/* configure interrupts coalescing/mitigation */
|
|
|
|
NFE_WRITE(sc, NFE_IMTIMER, NFE_IM_DEFAULT);
|
|
|
|
#else
|
|
|
|
/* no interrupt mitigation: one interrupt per packet */
|
|
|
|
NFE_WRITE(sc, NFE_IMTIMER, 970);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_SETUP_R1, NFE_R1_MAGIC);
|
|
|
|
NFE_WRITE(sc, NFE_SETUP_R2, NFE_R2_MAGIC);
|
|
|
|
NFE_WRITE(sc, NFE_SETUP_R6, NFE_R6_MAGIC);
|
|
|
|
|
|
|
|
/* update MAC knowledge of PHY; generates a NFE_IRQ_LINK interrupt */
|
|
|
|
NFE_WRITE(sc, NFE_STATUS, sc->mii_phyaddr << 24 | NFE_STATUS_MAGIC);
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_SETUP_R4, NFE_R4_MAGIC);
|
2008-04-17 23:12:26 +04:00
|
|
|
NFE_WRITE(sc, NFE_WOL_CTL, NFE_WOL_ENABLE);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
sc->rxtxctl &= ~NFE_RXTX_BIT2;
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, sc->rxtxctl);
|
|
|
|
DELAY(10);
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_BIT1 | sc->rxtxctl);
|
|
|
|
|
|
|
|
/* set Rx filter */
|
|
|
|
nfe_setmulti(sc);
|
|
|
|
|
Make many ethernet drivers share the common code for MII media
handling, ether_mediastatus() and ether_mediachange(). Check for
a non-ENXIO error return from mii_mediachg(). (ENXIO indicates
that a PHY is suspended.)
This patch shrinks the source code size by 979 lines. There was
a 5100-byte savings on the NetBSD/i386 kernel configuration, ALL.
I have made a few miscellaneous changes, too:
gem(4): use LIST_EMPTY(), LIST_FOREACH().
mtd(4): handle media ioctls, for a change!
axe(4): do not track link status in sc->axe_link any longer
nfe(4), aue(4), axe(4), udav(4), url(4): do not reset all PHYs
on a change of media
Except for the change to mtd(4), no functional changes are intended.
XXX This patch affects more architectures than I can feasibly
XXX compile and run. I have compiled macppc, sparc64, i386. I
XXX have run the patches on i386 boxen with bnx(4) and sip(4).
XXX Compiling and running on evbmips (MERAKI, ADM5120) is in
XXX progress.
2008-01-20 01:10:14 +03:00
|
|
|
if ((rc = ether_mediachange(ifp)) != 0)
|
|
|
|
goto out;
|
2006-03-13 01:40:42 +03:00
|
|
|
|
2007-01-05 04:33:57 +03:00
|
|
|
nfe_tick(sc);
|
|
|
|
|
2006-03-13 01:40:42 +03:00
|
|
|
/* enable Rx */
|
|
|
|
NFE_WRITE(sc, NFE_RX_CTL, NFE_RX_START);
|
|
|
|
|
|
|
|
/* enable Tx */
|
|
|
|
NFE_WRITE(sc, NFE_TX_CTL, NFE_TX_START);
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_PHY_STATUS, 0xf);
|
|
|
|
|
|
|
|
/* enable interrupts */
|
|
|
|
NFE_WRITE(sc, NFE_IRQ_MASK, NFE_IRQ_WANTED);
|
|
|
|
|
|
|
|
callout_schedule(&sc->sc_tick_ch, hz);
|
|
|
|
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
|
Make many ethernet drivers share the common code for MII media
handling, ether_mediastatus() and ether_mediachange(). Check for
a non-ENXIO error return from mii_mediachg(). (ENXIO indicates
that a PHY is suspended.)
This patch shrinks the source code size by 979 lines. There was
a 5100-byte savings on the NetBSD/i386 kernel configuration, ALL.
I have made a few miscellaneous changes, too:
gem(4): use LIST_EMPTY(), LIST_FOREACH().
mtd(4): handle media ioctls, for a change!
axe(4): do not track link status in sc->axe_link any longer
nfe(4), aue(4), axe(4), udav(4), url(4): do not reset all PHYs
on a change of media
Except for the change to mtd(4), no functional changes are intended.
XXX This patch affects more architectures than I can feasibly
XXX compile and run. I have compiled macppc, sparc64, i386. I
XXX have run the patches on i386 boxen with bnx(4) and sip(4).
XXX Compiling and running on evbmips (MERAKI, ADM5120) is in
XXX progress.
2008-01-20 01:10:14 +03:00
|
|
|
out:
|
|
|
|
return rc;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-11-16 04:32:37 +03:00
|
|
|
nfe_stop(struct ifnet *ifp, int disable)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
|
|
|
struct nfe_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
callout_stop(&sc->sc_tick_ch);
|
|
|
|
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
|
|
|
|
|
|
|
|
mii_down(&sc->sc_mii);
|
|
|
|
|
|
|
|
/* abort Tx */
|
|
|
|
NFE_WRITE(sc, NFE_TX_CTL, 0);
|
|
|
|
|
|
|
|
/* disable Rx */
|
|
|
|
NFE_WRITE(sc, NFE_RX_CTL, 0);
|
|
|
|
|
|
|
|
/* disable interrupts */
|
|
|
|
NFE_WRITE(sc, NFE_IRQ_MASK, 0);
|
|
|
|
|
|
|
|
/* reset Tx and Rx rings */
|
|
|
|
nfe_reset_tx_ring(sc, &sc->txq);
|
|
|
|
nfe_reset_rx_ring(sc, &sc->rxq);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_alloc_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring)
|
|
|
|
{
|
|
|
|
struct nfe_desc32 *desc32;
|
|
|
|
struct nfe_desc64 *desc64;
|
|
|
|
struct nfe_rx_data *data;
|
|
|
|
struct nfe_jbuf *jbuf;
|
|
|
|
void **desc;
|
|
|
|
bus_addr_t physaddr;
|
|
|
|
int i, nsegs, error, descsize;
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc = (void **)&ring->desc64;
|
|
|
|
descsize = sizeof (struct nfe_desc64);
|
|
|
|
} else {
|
|
|
|
desc = (void **)&ring->desc32;
|
|
|
|
descsize = sizeof (struct nfe_desc32);
|
|
|
|
}
|
|
|
|
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
ring->bufsz = MCLBYTES;
|
|
|
|
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, NFE_RX_RING_COUNT * descsize, 1,
|
|
|
|
NFE_RX_RING_COUNT * descsize, 0, BUS_DMA_NOWAIT, &ring->map);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create desc DMA map\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
ring->map = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->sc_dmat, NFE_RX_RING_COUNT * descsize,
|
|
|
|
PAGE_SIZE, 0, &ring->seg, 1, &nsegs, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs,
|
2007-03-04 08:59:00 +03:00
|
|
|
NFE_RX_RING_COUNT * descsize, (void **)desc, BUS_DMA_NOWAIT);
|
2006-03-13 01:40:42 +03:00
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map desc DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamap_load(sc->sc_dmat, ring->map, *desc,
|
|
|
|
NFE_RX_RING_COUNT * descsize, NULL, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load desc DMA map\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2009-03-01 16:44:54 +03:00
|
|
|
memset(*desc, 0, NFE_RX_RING_COUNT * descsize);
|
2006-03-13 01:40:42 +03:00
|
|
|
ring->physaddr = ring->map->dm_segs[0].ds_addr;
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_USE_JUMBO) {
|
|
|
|
ring->bufsz = NFE_JBYTES;
|
|
|
|
if ((error = nfe_jpool_alloc(sc)) != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate jumbo frames\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pre-allocate Rx buffers and populate Rx ring.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < NFE_RX_RING_COUNT; i++) {
|
|
|
|
data = &sc->rxq.data[i];
|
|
|
|
|
|
|
|
MGETHDR(data->m, M_DONTWAIT, MT_DATA);
|
|
|
|
if (data->m == NULL) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate rx mbuf\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
error = ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_USE_JUMBO) {
|
2007-09-24 17:17:53 +04:00
|
|
|
if ((jbuf = nfe_jalloc(sc, i)) == NULL) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate jumbo buffer\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
MEXTADD(data->m, jbuf->buf, NFE_JBYTES, 0, nfe_jfree,
|
|
|
|
sc);
|
|
|
|
|
|
|
|
physaddr = jbuf->physaddr;
|
|
|
|
} else {
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1,
|
|
|
|
MCLBYTES, 0, BUS_DMA_NOWAIT, &data->map);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create DMA map\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
data->map = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
MCLGET(data->m, M_DONTWAIT);
|
|
|
|
if (!(data->m->m_flags & M_EXT)) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate mbuf cluster\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
error = ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamap_load(sc->sc_dmat, data->map,
|
|
|
|
mtod(data->m, void *), MCLBYTES, NULL,
|
|
|
|
BUS_DMA_READ | BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load rx buf DMA map");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
physaddr = data->map->dm_segs[0].ds_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc64 = &sc->rxq.desc64[i];
|
|
|
|
#if defined(__LP64__)
|
|
|
|
desc64->physaddr[0] = htole32(physaddr >> 32);
|
|
|
|
#endif
|
|
|
|
desc64->physaddr[1] = htole32(physaddr & 0xffffffff);
|
|
|
|
desc64->length = htole16(sc->rxq.bufsz);
|
|
|
|
desc64->flags = htole16(NFE_RX_READY);
|
|
|
|
} else {
|
|
|
|
desc32 = &sc->rxq.desc32[i];
|
|
|
|
desc32->physaddr = htole32(physaddr);
|
|
|
|
desc32->length = htole16(sc->rxq.bufsz);
|
|
|
|
desc32->flags = htole16(NFE_RX_READY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail: nfe_free_rx_ring(sc, ring);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_reset_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NFE_RX_RING_COUNT; i++) {
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
ring->desc64[i].length = htole16(ring->bufsz);
|
|
|
|
ring->desc64[i].flags = htole16(NFE_RX_READY);
|
|
|
|
} else {
|
|
|
|
ring->desc32[i].length = htole16(ring->bufsz);
|
|
|
|
ring->desc32[i].flags = htole16(NFE_RX_READY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_free_rx_ring(struct nfe_softc *sc, struct nfe_rx_ring *ring)
|
|
|
|
{
|
|
|
|
struct nfe_rx_data *data;
|
|
|
|
void *desc;
|
|
|
|
int i, descsize;
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc = ring->desc64;
|
|
|
|
descsize = sizeof (struct nfe_desc64);
|
|
|
|
} else {
|
|
|
|
desc = ring->desc32;
|
|
|
|
descsize = sizeof (struct nfe_desc32);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desc != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->map, 0,
|
|
|
|
ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->map);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)desc,
|
2006-03-13 01:40:42 +03:00
|
|
|
NFE_RX_RING_COUNT * descsize);
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &ring->seg, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < NFE_RX_RING_COUNT; i++) {
|
|
|
|
data = &ring->data[i];
|
|
|
|
|
|
|
|
if (data->map != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
|
|
|
data->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->map);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, data->map);
|
|
|
|
}
|
|
|
|
if (data->m != NULL)
|
|
|
|
m_freem(data->m);
|
|
|
|
}
|
2010-11-03 17:03:40 +03:00
|
|
|
|
|
|
|
nfe_jpool_free(sc);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
struct nfe_jbuf *
|
2007-09-24 17:17:53 +04:00
|
|
|
nfe_jalloc(struct nfe_softc *sc, int i)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
|
|
|
struct nfe_jbuf *jbuf;
|
|
|
|
|
2008-04-20 12:57:37 +04:00
|
|
|
mutex_enter(&sc->rxq.mtx);
|
2006-03-13 01:40:42 +03:00
|
|
|
jbuf = SLIST_FIRST(&sc->rxq.jfreelist);
|
2008-04-20 12:57:37 +04:00
|
|
|
if (jbuf != NULL)
|
|
|
|
SLIST_REMOVE_HEAD(&sc->rxq.jfreelist, jnext);
|
|
|
|
mutex_exit(&sc->rxq.mtx);
|
2006-03-13 01:40:42 +03:00
|
|
|
if (jbuf == NULL)
|
|
|
|
return NULL;
|
2007-09-24 17:17:53 +04:00
|
|
|
sc->rxq.jbufmap[i] =
|
|
|
|
((char *)jbuf->buf - (char *)sc->rxq.jpool) / NFE_JBYTES;
|
2006-03-13 01:40:42 +03:00
|
|
|
return jbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called automatically by the network stack when the mbuf is freed.
|
|
|
|
* Caution must be taken that the NIC might be reset by the time the mbuf is
|
|
|
|
* freed.
|
|
|
|
*/
|
|
|
|
void
|
2007-03-04 08:59:00 +03:00
|
|
|
nfe_jfree(struct mbuf *m, void *buf, size_t size, void *arg)
|
2006-03-13 01:40:42 +03:00
|
|
|
{
|
|
|
|
struct nfe_softc *sc = arg;
|
|
|
|
struct nfe_jbuf *jbuf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* find the jbuf from the base pointer */
|
2007-03-04 08:59:00 +03:00
|
|
|
i = ((char *)buf - (char *)sc->rxq.jpool) / NFE_JBYTES;
|
2006-03-13 01:40:42 +03:00
|
|
|
if (i < 0 || i >= NFE_JPOOL_COUNT) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"request to free a buffer (%p) not managed by us\n", buf);
|
2006-03-13 01:40:42 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
jbuf = &sc->rxq.jbuf[i];
|
|
|
|
|
|
|
|
/* ..and put it back in the free list */
|
2008-04-20 12:57:37 +04:00
|
|
|
mutex_enter(&sc->rxq.mtx);
|
2006-03-13 01:40:42 +03:00
|
|
|
SLIST_INSERT_HEAD(&sc->rxq.jfreelist, jbuf, jnext);
|
2008-04-20 12:57:37 +04:00
|
|
|
mutex_exit(&sc->rxq.mtx);
|
2006-03-16 20:26:13 +03:00
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
if (m != NULL)
|
|
|
|
pool_cache_put(mb_cache, m);
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_jpool_alloc(struct nfe_softc *sc)
|
|
|
|
{
|
|
|
|
struct nfe_rx_ring *ring = &sc->rxq;
|
|
|
|
struct nfe_jbuf *jbuf;
|
|
|
|
bus_addr_t physaddr;
|
2007-03-04 08:59:00 +03:00
|
|
|
char *buf;
|
2006-03-13 01:40:42 +03:00
|
|
|
int i, nsegs, error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a big chunk of DMA'able memory.
|
|
|
|
*/
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, NFE_JPOOL_SIZE, 1,
|
|
|
|
NFE_JPOOL_SIZE, 0, BUS_DMA_NOWAIT, &ring->jmap);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create jumbo DMA map\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
ring->jmap = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->sc_dmat, NFE_JPOOL_SIZE, PAGE_SIZE, 0,
|
|
|
|
&ring->jseg, 1, &nsegs, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate jumbo DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_map(sc->sc_dmat, &ring->jseg, nsegs, NFE_JPOOL_SIZE,
|
|
|
|
&ring->jpool, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map jumbo DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamap_load(sc->sc_dmat, ring->jmap, ring->jpool,
|
|
|
|
NFE_JPOOL_SIZE, NULL, BUS_DMA_READ | BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not load jumbo DMA map\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ..and split it into 9KB chunks */
|
|
|
|
SLIST_INIT(&ring->jfreelist);
|
|
|
|
|
|
|
|
buf = ring->jpool;
|
|
|
|
physaddr = ring->jmap->dm_segs[0].ds_addr;
|
|
|
|
for (i = 0; i < NFE_JPOOL_COUNT; i++) {
|
|
|
|
jbuf = &ring->jbuf[i];
|
|
|
|
|
|
|
|
jbuf->buf = buf;
|
|
|
|
jbuf->physaddr = physaddr;
|
|
|
|
|
|
|
|
SLIST_INSERT_HEAD(&ring->jfreelist, jbuf, jnext);
|
|
|
|
|
|
|
|
buf += NFE_JBYTES;
|
|
|
|
physaddr += NFE_JBYTES;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail: nfe_jpool_free(sc);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_jpool_free(struct nfe_softc *sc)
|
|
|
|
{
|
|
|
|
struct nfe_rx_ring *ring = &sc->rxq;
|
|
|
|
|
|
|
|
if (ring->jmap != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->jmap, 0,
|
|
|
|
ring->jmap->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->jmap);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, ring->jmap);
|
2010-11-03 17:03:40 +03:00
|
|
|
ring->jmap = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
if (ring->jpool != NULL) {
|
|
|
|
bus_dmamem_unmap(sc->sc_dmat, ring->jpool, NFE_JPOOL_SIZE);
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &ring->jseg, 1);
|
2010-11-03 17:03:40 +03:00
|
|
|
ring->jpool = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
nfe_alloc_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring)
|
|
|
|
{
|
|
|
|
int i, nsegs, error;
|
|
|
|
void **desc;
|
|
|
|
int descsize;
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc = (void **)&ring->desc64;
|
|
|
|
descsize = sizeof (struct nfe_desc64);
|
|
|
|
} else {
|
|
|
|
desc = (void **)&ring->desc32;
|
|
|
|
descsize = sizeof (struct nfe_desc32);
|
|
|
|
}
|
|
|
|
|
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, NFE_TX_RING_COUNT * descsize, 1,
|
|
|
|
NFE_TX_RING_COUNT * descsize, 0, BUS_DMA_NOWAIT, &ring->map);
|
|
|
|
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create desc DMA map\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
ring->map = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->sc_dmat, NFE_TX_RING_COUNT * descsize,
|
|
|
|
PAGE_SIZE, 0, &ring->seg, 1, &nsegs, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not allocate DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs,
|
2007-03-04 08:59:00 +03:00
|
|
|
NFE_TX_RING_COUNT * descsize, (void **)desc, BUS_DMA_NOWAIT);
|
2006-03-13 01:40:42 +03:00
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not map desc DMA memory\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamap_load(sc->sc_dmat, ring->map, *desc,
|
|
|
|
NFE_TX_RING_COUNT * descsize, NULL, BUS_DMA_NOWAIT);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev, "could not load desc DMA map\n");
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2009-03-01 16:44:54 +03:00
|
|
|
memset(*desc, 0, NFE_TX_RING_COUNT * descsize);
|
2006-03-13 01:40:42 +03:00
|
|
|
ring->physaddr = ring->map->dm_segs[0].ds_addr;
|
|
|
|
|
|
|
|
for (i = 0; i < NFE_TX_RING_COUNT; i++) {
|
|
|
|
error = bus_dmamap_create(sc->sc_dmat, NFE_JBYTES,
|
|
|
|
NFE_MAX_SCATTER, NFE_JBYTES, 0, BUS_DMA_NOWAIT,
|
|
|
|
&ring->data[i].map);
|
|
|
|
if (error != 0) {
|
2008-03-26 17:46:21 +03:00
|
|
|
aprint_error_dev(sc->sc_dev,
|
|
|
|
"could not create DMA map\n");
|
2009-03-01 16:34:10 +03:00
|
|
|
ring->data[i].map = NULL;
|
2006-03-13 01:40:42 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail: nfe_free_tx_ring(sc, ring);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_reset_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring)
|
|
|
|
{
|
|
|
|
struct nfe_tx_data *data;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NFE_TX_RING_COUNT; i++) {
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR)
|
|
|
|
ring->desc64[i].flags = 0;
|
|
|
|
else
|
|
|
|
ring->desc32[i].flags = 0;
|
|
|
|
|
|
|
|
data = &ring->data[i];
|
|
|
|
|
|
|
|
if (data->m != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->active, 0,
|
|
|
|
data->active->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->active);
|
|
|
|
m_freem(data->m);
|
|
|
|
data->m = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
ring->queued = 0;
|
|
|
|
ring->cur = ring->next = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_free_tx_ring(struct nfe_softc *sc, struct nfe_tx_ring *ring)
|
|
|
|
{
|
|
|
|
struct nfe_tx_data *data;
|
|
|
|
void *desc;
|
|
|
|
int i, descsize;
|
|
|
|
|
|
|
|
if (sc->sc_flags & NFE_40BIT_ADDR) {
|
|
|
|
desc = ring->desc64;
|
|
|
|
descsize = sizeof (struct nfe_desc64);
|
|
|
|
} else {
|
|
|
|
desc = ring->desc32;
|
|
|
|
descsize = sizeof (struct nfe_desc32);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desc != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, ring->map, 0,
|
|
|
|
ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, ring->map);
|
2007-03-04 08:59:00 +03:00
|
|
|
bus_dmamem_unmap(sc->sc_dmat, (void *)desc,
|
2006-03-13 01:40:42 +03:00
|
|
|
NFE_TX_RING_COUNT * descsize);
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &ring->seg, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < NFE_TX_RING_COUNT; i++) {
|
|
|
|
data = &ring->data[i];
|
|
|
|
|
|
|
|
if (data->m != NULL) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, data->active, 0,
|
|
|
|
data->active->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, data->active);
|
|
|
|
m_freem(data->m);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ..and now actually destroy the DMA mappings */
|
|
|
|
for (i = 0; i < NFE_TX_RING_COUNT; i++) {
|
|
|
|
data = &ring->data[i];
|
|
|
|
if (data->map == NULL)
|
|
|
|
continue;
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, data->map);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_setmulti(struct nfe_softc *sc)
|
|
|
|
{
|
|
|
|
struct ethercom *ec = &sc->sc_ethercom;
|
|
|
|
struct ifnet *ifp = &ec->ec_if;
|
|
|
|
struct ether_multi *enm;
|
|
|
|
struct ether_multistep step;
|
|
|
|
uint8_t addr[ETHER_ADDR_LEN], mask[ETHER_ADDR_LEN];
|
|
|
|
uint32_t filter = NFE_RXFILTER_MAGIC;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((ifp->if_flags & (IFF_ALLMULTI | IFF_PROMISC)) != 0) {
|
2009-03-01 16:44:54 +03:00
|
|
|
memset(addr, 0, ETHER_ADDR_LEN);
|
|
|
|
memset(mask, 0, ETHER_ADDR_LEN);
|
2006-03-13 01:40:42 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2009-03-01 16:44:54 +03:00
|
|
|
memcpy(addr, etherbroadcastaddr, ETHER_ADDR_LEN);
|
|
|
|
memcpy(mask, etherbroadcastaddr, ETHER_ADDR_LEN);
|
2006-03-13 01:40:42 +03:00
|
|
|
|
|
|
|
ETHER_FIRST_MULTI(step, ec, enm);
|
|
|
|
while (enm != NULL) {
|
2009-03-18 18:14:29 +03:00
|
|
|
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
|
2006-03-13 01:40:42 +03:00
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
2009-03-01 16:44:54 +03:00
|
|
|
memset(addr, 0, ETHER_ADDR_LEN);
|
|
|
|
memset(mask, 0, ETHER_ADDR_LEN);
|
2006-03-13 01:40:42 +03:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
|
|
addr[i] &= enm->enm_addrlo[i];
|
|
|
|
mask[i] &= ~enm->enm_addrlo[i];
|
|
|
|
}
|
|
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
|
|
}
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++)
|
|
|
|
mask[i] |= addr[i];
|
|
|
|
|
|
|
|
done:
|
|
|
|
addr[0] |= 0x01; /* make sure multicast bit is set */
|
|
|
|
|
|
|
|
NFE_WRITE(sc, NFE_MULTIADDR_HI,
|
|
|
|
addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]);
|
|
|
|
NFE_WRITE(sc, NFE_MULTIADDR_LO,
|
|
|
|
addr[5] << 8 | addr[4]);
|
|
|
|
NFE_WRITE(sc, NFE_MULTIMASK_HI,
|
|
|
|
mask[3] << 24 | mask[2] << 16 | mask[1] << 8 | mask[0]);
|
|
|
|
NFE_WRITE(sc, NFE_MULTIMASK_LO,
|
|
|
|
mask[5] << 8 | mask[4]);
|
|
|
|
|
|
|
|
filter |= (ifp->if_flags & IFF_PROMISC) ? NFE_PROMISC : NFE_U2M;
|
|
|
|
NFE_WRITE(sc, NFE_RXFILTER, filter);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_get_macaddr(struct nfe_softc *sc, uint8_t *addr)
|
|
|
|
{
|
|
|
|
uint32_t tmp;
|
|
|
|
|
2008-04-17 23:12:26 +04:00
|
|
|
if ((sc->sc_flags & NFE_CORRECT_MACADDR) != 0) {
|
|
|
|
tmp = NFE_READ(sc, NFE_MACADDR_HI);
|
|
|
|
addr[0] = (tmp & 0xff);
|
|
|
|
addr[1] = (tmp >> 8) & 0xff;
|
|
|
|
addr[2] = (tmp >> 16) & 0xff;
|
|
|
|
addr[3] = (tmp >> 24) & 0xff;
|
|
|
|
|
|
|
|
tmp = NFE_READ(sc, NFE_MACADDR_LO);
|
|
|
|
addr[4] = (tmp & 0xff);
|
|
|
|
addr[5] = (tmp >> 8) & 0xff;
|
|
|
|
|
|
|
|
} else {
|
2007-12-17 15:41:06 +03:00
|
|
|
tmp = NFE_READ(sc, NFE_MACADDR_LO);
|
|
|
|
addr[0] = (tmp >> 8) & 0xff;
|
|
|
|
addr[1] = (tmp & 0xff);
|
|
|
|
|
|
|
|
tmp = NFE_READ(sc, NFE_MACADDR_HI);
|
|
|
|
addr[2] = (tmp >> 24) & 0xff;
|
|
|
|
addr[3] = (tmp >> 16) & 0xff;
|
|
|
|
addr[4] = (tmp >> 8) & 0xff;
|
|
|
|
addr[5] = (tmp & 0xff);
|
|
|
|
}
|
2006-03-13 01:40:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_set_macaddr(struct nfe_softc *sc, const uint8_t *addr)
|
|
|
|
{
|
|
|
|
NFE_WRITE(sc, NFE_MACADDR_LO,
|
|
|
|
addr[5] << 8 | addr[4]);
|
|
|
|
NFE_WRITE(sc, NFE_MACADDR_HI,
|
|
|
|
addr[3] << 24 | addr[2] << 16 | addr[1] << 8 | addr[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nfe_tick(void *arg)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = arg;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
mii_tick(&sc->sc_mii);
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
callout_schedule(&sc->sc_tick_ch, hz);
|
|
|
|
}
|
2008-05-26 02:57:35 +04:00
|
|
|
|
|
|
|
void
|
|
|
|
nfe_poweron(device_t self)
|
|
|
|
{
|
|
|
|
struct nfe_softc *sc = device_private(self);
|
|
|
|
|
|
|
|
if ((sc->sc_flags & NFE_PWR_MGMT) != 0) {
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_RESET | NFE_RXTX_BIT2);
|
|
|
|
NFE_WRITE(sc, NFE_MAC_RESET, NFE_MAC_RESET_MAGIC);
|
|
|
|
DELAY(100);
|
|
|
|
NFE_WRITE(sc, NFE_MAC_RESET, 0);
|
|
|
|
DELAY(100);
|
|
|
|
NFE_WRITE(sc, NFE_RXTX_CTL, NFE_RXTX_BIT2);
|
|
|
|
NFE_WRITE(sc, NFE_PWR2_CTL,
|
|
|
|
NFE_READ(sc, NFE_PWR2_CTL) & ~NFE_PWR2_WAKEUP_MASK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-02-25 01:37:54 +03:00
|
|
|
nfe_resume(device_t dv, const pmf_qual_t *qual)
|
2008-05-26 02:57:35 +04:00
|
|
|
{
|
|
|
|
nfe_poweron(dv);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|