2010-04-05 11:19:28 +04:00
|
|
|
/* $NetBSD: if_txp.c,v 1.37 2010/04/05 07:20:27 joerg Exp $ */
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2001
|
|
|
|
* Jason L. Wright <jason@thought.net>, Theo de Raadt, and
|
|
|
|
* Aaron Campbell <aaron@monkey.org>. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
|
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR THE VOICES IN THEIR HEADS
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Driver for 3c990 (Typhoon) Ethernet ASIC
|
|
|
|
*/
|
|
|
|
|
2003-07-14 19:47:00 +04:00
|
|
|
#include <sys/cdefs.h>
|
2010-04-05 11:19:28 +04:00
|
|
|
__KERNEL_RCSID(0, "$NetBSD: if_txp.c,v 1.37 2010/04/05 07:20:27 joerg Exp $");
|
2003-07-14 19:47:00 +04:00
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
#include "opt_inet.h"
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/callout.h>
|
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_types.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
|
|
|
|
|
|
|
|
#include <net/if_media.h>
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
2006-11-04 08:54:53 +03:00
|
|
|
#include <uvm/uvm_extern.h> /* for PAGE_SIZE */
|
2007-10-19 15:59:34 +04:00
|
|
|
#include <sys/bus.h>
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
#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_txpreg.h>
|
|
|
|
|
|
|
|
#include <dev/microcode/typhoon/3c990img.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These currently break the 3c990 firmware, hopefully will be resolved
|
|
|
|
* at some point.
|
|
|
|
*/
|
|
|
|
#undef TRY_TX_UDP_CSUM
|
|
|
|
#undef TRY_TX_TCP_CSUM
|
|
|
|
|
2008-04-09 21:32:01 +04:00
|
|
|
int txp_probe(device_t, cfdata_t, void *);
|
|
|
|
void txp_attach(device_t, device_t, void *);
|
2003-07-02 00:08:51 +04:00
|
|
|
int txp_intr(void *);
|
|
|
|
void txp_tick(void *);
|
2009-09-27 16:52:59 +04:00
|
|
|
bool txp_shutdown(device_t, int);
|
2007-03-04 08:59:00 +03:00
|
|
|
int txp_ioctl(struct ifnet *, u_long, void *);
|
2003-07-02 00:08:51 +04:00
|
|
|
void txp_start(struct ifnet *);
|
|
|
|
void txp_stop(struct txp_softc *);
|
|
|
|
void txp_init(struct txp_softc *);
|
|
|
|
void txp_watchdog(struct ifnet *);
|
|
|
|
|
|
|
|
int txp_chip_init(struct txp_softc *);
|
|
|
|
int txp_reset_adapter(struct txp_softc *);
|
|
|
|
int txp_download_fw(struct txp_softc *);
|
|
|
|
int txp_download_fw_wait(struct txp_softc *);
|
|
|
|
int txp_download_fw_section(struct txp_softc *,
|
2005-06-18 20:10:44 +04:00
|
|
|
const struct txp_fw_section_header *, int);
|
2003-07-02 00:08:51 +04:00
|
|
|
int txp_alloc_rings(struct txp_softc *);
|
|
|
|
void txp_dma_free(struct txp_softc *, struct txp_dma_alloc *);
|
|
|
|
int txp_dma_malloc(struct txp_softc *, bus_size_t, struct txp_dma_alloc *, int);
|
|
|
|
void txp_set_filter(struct txp_softc *);
|
|
|
|
|
|
|
|
int txp_cmd_desc_numfree(struct txp_softc *);
|
|
|
|
int txp_command(struct txp_softc *, u_int16_t, u_int16_t, u_int32_t,
|
|
|
|
u_int32_t, u_int16_t *, u_int32_t *, u_int32_t *, int);
|
|
|
|
int txp_command2(struct txp_softc *, u_int16_t, u_int16_t,
|
|
|
|
u_int32_t, u_int32_t, struct txp_ext_desc *, u_int8_t,
|
|
|
|
struct txp_rsp_desc **, int);
|
|
|
|
int txp_response(struct txp_softc *, u_int32_t, u_int16_t, u_int16_t,
|
|
|
|
struct txp_rsp_desc **);
|
|
|
|
void txp_rsp_fixup(struct txp_softc *, struct txp_rsp_desc *,
|
|
|
|
struct txp_rsp_desc *);
|
|
|
|
void txp_capabilities(struct txp_softc *);
|
|
|
|
|
|
|
|
void txp_ifmedia_sts(struct ifnet *, struct ifmediareq *);
|
|
|
|
int txp_ifmedia_upd(struct ifnet *);
|
|
|
|
void txp_show_descriptor(void *);
|
|
|
|
void txp_tx_reclaim(struct txp_softc *, struct txp_tx_ring *,
|
|
|
|
struct txp_dma_alloc *);
|
|
|
|
void txp_rxbuf_reclaim(struct txp_softc *);
|
|
|
|
void txp_rx_reclaim(struct txp_softc *, struct txp_rx_ring *,
|
|
|
|
struct txp_dma_alloc *);
|
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
CFATTACH_DECL(txp, sizeof(struct txp_softc), txp_probe, txp_attach,
|
|
|
|
NULL, NULL);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
const struct txp_pci_match {
|
|
|
|
int vid, did, flags;
|
2003-07-02 00:08:51 +04:00
|
|
|
} txp_devices[] = {
|
2003-07-07 19:18:24 +04:00
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990, 0 },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990TX95, 0 },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990TX97, 0 },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990SVR95, TXP_SERVERVERSION },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990SVR97, TXP_SERVERVERSION },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C990B, TXP_USESUBSYSTEM },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3C990BSVR, TXP_SERVERVERSION },
|
|
|
|
{ PCI_VENDOR_3COM, PCI_PRODUCT_3COM_3CR990FX, TXP_USESUBSYSTEM },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct txp_pci_match *txp_pcilookup(pcireg_t);
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
u_int16_t mask, value;
|
|
|
|
int flags;
|
|
|
|
} txp_subsysinfo[] = {
|
|
|
|
{0xf000, 0x2000, TXP_SERVERVERSION},
|
|
|
|
{0x0100, 0x0100, TXP_FIBER},
|
|
|
|
#if 0 /* information from 3com header, unused */
|
|
|
|
{0x0010, 0x0010, /* secured firmware */},
|
|
|
|
{0x0003, 0x0000, /* variable DES */},
|
|
|
|
{0x0003, 0x0001, /* single DES - "95" */},
|
|
|
|
{0x0003, 0x0002, /* triple DES - "97" */},
|
|
|
|
#endif
|
2003-07-02 00:08:51 +04:00
|
|
|
};
|
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
static const struct txp_pci_match *
|
2008-04-09 21:32:01 +04:00
|
|
|
txp_pcilookup(pcireg_t id)
|
2003-07-07 19:18:24 +04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2008-04-09 21:32:01 +04:00
|
|
|
for (i = 0; i < __arraycount(txp_devices); i++)
|
|
|
|
if (PCI_VENDOR(id) == txp_devices[i].vid &&
|
|
|
|
PCI_PRODUCT(id) == txp_devices[i].did)
|
|
|
|
return &txp_devices[i];
|
2003-07-07 19:18:24 +04:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
int
|
2008-04-09 21:32:01 +04:00
|
|
|
txp_probe(device_t parent, cfdata_t match, void *aux)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
if (txp_pcilookup(pa->pa_id))
|
2003-07-02 00:08:51 +04:00
|
|
|
return (1);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2008-04-09 21:32:01 +04:00
|
|
|
txp_attach(device_t parent, device_t self, void *aux)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
2008-04-09 21:32:01 +04:00
|
|
|
struct txp_softc *sc = device_private(self);
|
2003-07-02 00:08:51 +04:00
|
|
|
struct pci_attach_args *pa = aux;
|
|
|
|
pci_chipset_tag_t pc = pa->pa_pc;
|
|
|
|
pci_intr_handle_t ih;
|
|
|
|
const char *intrstr = NULL;
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
u_int32_t command;
|
|
|
|
u_int16_t p1;
|
|
|
|
u_int32_t p2;
|
|
|
|
u_char enaddr[6];
|
2008-04-09 21:27:14 +04:00
|
|
|
const struct txp_pci_match *match;
|
2003-07-07 19:18:24 +04:00
|
|
|
u_int16_t subsys;
|
|
|
|
int i, flags;
|
|
|
|
char devinfo[256];
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
sc->sc_cold = 1;
|
|
|
|
|
2008-04-09 21:27:14 +04:00
|
|
|
match = txp_pcilookup(pa->pa_id);
|
|
|
|
flags = match->flags;
|
|
|
|
if (match->flags & TXP_USESUBSYSTEM) {
|
2003-07-07 19:18:24 +04:00
|
|
|
subsys = PCI_PRODUCT(pci_conf_read(pc, pa->pa_tag,
|
|
|
|
PCI_SUBSYS_ID_REG));
|
|
|
|
for (i = 0;
|
|
|
|
i < sizeof(txp_subsysinfo)/sizeof(txp_subsysinfo[0]);
|
|
|
|
i++)
|
|
|
|
if ((subsys & txp_subsysinfo[i].mask) ==
|
|
|
|
txp_subsysinfo[i].value)
|
|
|
|
flags |= txp_subsysinfo[i].flags;
|
|
|
|
}
|
|
|
|
sc->sc_flags = flags;
|
|
|
|
|
2004-04-24 01:13:05 +04:00
|
|
|
pci_devinfo(pa->pa_id, 0, 0, devinfo, sizeof(devinfo));
|
2003-07-07 19:18:24 +04:00
|
|
|
#define TXP_EXTRAINFO ((flags & (TXP_USESUBSYSTEM|TXP_SERVERVERSION)) == \
|
|
|
|
(TXP_USESUBSYSTEM|TXP_SERVERVERSION) ? " (SVR)" : "")
|
2008-04-10 23:13:36 +04:00
|
|
|
printf(": %s%s\n%s", devinfo, TXP_EXTRAINFO, device_xname(&sc->sc_dev));
|
2003-07-07 19:18:24 +04:00
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
command = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
|
|
|
|
if (!(command & PCI_COMMAND_MASTER_ENABLE)) {
|
|
|
|
printf(": failed to enable bus mastering\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(command & PCI_COMMAND_MEM_ENABLE)) {
|
|
|
|
printf(": failed to enable memory mapping\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pci_mapreg_map(pa, TXP_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0,
|
|
|
|
&sc->sc_bt, &sc->sc_bh, NULL, NULL)) {
|
|
|
|
printf(": can't map mem space %d\n", 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_dmat = pa->pa_dmat;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate our interrupt.
|
|
|
|
*/
|
|
|
|
if (pci_intr_map(pa, &ih)) {
|
|
|
|
printf(": couldn't map interrupt\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
intrstr = pci_intr_string(pc, ih);
|
|
|
|
sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, txp_intr, sc);
|
|
|
|
if (sc->sc_ih == NULL) {
|
|
|
|
printf(": couldn't establish interrupt");
|
|
|
|
if (intrstr != NULL)
|
|
|
|
printf(" at %s", intrstr);
|
|
|
|
printf("\n");
|
|
|
|
return;
|
|
|
|
}
|
2003-07-07 19:18:24 +04:00
|
|
|
printf(": interrupting at %s\n", intrstr);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (txp_chip_init(sc))
|
2004-07-01 12:34:24 +04:00
|
|
|
goto cleanupintr;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (txp_download_fw(sc))
|
2004-07-01 12:34:24 +04:00
|
|
|
goto cleanupintr;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (txp_alloc_rings(sc))
|
2004-07-01 12:34:24 +04:00
|
|
|
goto cleanupintr;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_MAX_PKT_SIZE_WRITE, TXP_MAX_PKTLEN, 0, 0,
|
|
|
|
NULL, NULL, NULL, 1))
|
2004-07-01 12:34:24 +04:00
|
|
|
goto cleanupintr;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_STATION_ADDRESS_READ, 0, 0, 0,
|
|
|
|
&p1, &p2, NULL, 1))
|
2004-07-01 12:34:24 +04:00
|
|
|
goto cleanupintr;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
txp_set_filter(sc);
|
|
|
|
|
|
|
|
p1 = htole16(p1);
|
|
|
|
enaddr[0] = ((u_int8_t *)&p1)[1];
|
|
|
|
enaddr[1] = ((u_int8_t *)&p1)[0];
|
|
|
|
p2 = htole32(p2);
|
|
|
|
enaddr[2] = ((u_int8_t *)&p2)[3];
|
|
|
|
enaddr[3] = ((u_int8_t *)&p2)[2];
|
|
|
|
enaddr[4] = ((u_int8_t *)&p2)[1];
|
|
|
|
enaddr[5] = ((u_int8_t *)&p2)[0];
|
|
|
|
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: Ethernet address %s\n", device_xname(&sc->sc_dev),
|
2003-07-07 19:18:24 +04:00
|
|
|
ether_sprintf(enaddr));
|
2003-07-02 00:08:51 +04:00
|
|
|
sc->sc_cold = 0;
|
|
|
|
|
|
|
|
ifmedia_init(&sc->sc_ifmedia, 0, txp_ifmedia_upd, txp_ifmedia_sts);
|
2003-07-07 19:18:24 +04:00
|
|
|
if (flags & TXP_FIBER) {
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_FX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_FX|IFM_HDX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_FX|IFM_FDX,
|
|
|
|
0, NULL);
|
|
|
|
} else {
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_10_T|IFM_FDX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_HDX,
|
|
|
|
0, NULL);
|
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_100_TX|IFM_FDX,
|
|
|
|
0, NULL);
|
|
|
|
}
|
2003-07-02 00:08:51 +04:00
|
|
|
ifmedia_add(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
|
|
|
|
|
|
|
|
sc->sc_xcvr = TXP_XCVR_AUTO;
|
|
|
|
txp_command(sc, TXP_CMD_XCVR_SELECT, TXP_XCVR_AUTO, 0, 0,
|
|
|
|
NULL, NULL, NULL, 0);
|
|
|
|
ifmedia_set(&sc->sc_ifmedia, IFM_ETHER|IFM_AUTO);
|
|
|
|
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
ifp->if_mtu = ETHERMTU;
|
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_ioctl = txp_ioctl;
|
|
|
|
ifp->if_start = txp_start;
|
|
|
|
ifp->if_watchdog = txp_watchdog;
|
|
|
|
ifp->if_baudrate = 10000000;
|
|
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, TX_ENTRIES);
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
ifp->if_capabilities = 0;
|
2008-04-10 23:13:36 +04:00
|
|
|
strlcpy(ifp->if_xname, device_xname(&sc->sc_dev), IFNAMSIZ);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
txp_capabilities(sc);
|
|
|
|
|
2007-07-10 00:51:58 +04:00
|
|
|
callout_init(&sc->sc_tick, 0);
|
2003-07-02 00:08:51 +04:00
|
|
|
callout_setfunc(&sc->sc_tick, txp_tick, sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach us everywhere
|
|
|
|
*/
|
|
|
|
if_attach(ifp);
|
|
|
|
ether_ifattach(ifp, enaddr);
|
|
|
|
|
2009-09-27 16:52:59 +04:00
|
|
|
if (pmf_device_register1(self, NULL, NULL, txp_shutdown))
|
|
|
|
pmf_class_network_register(self, ifp);
|
|
|
|
else
|
|
|
|
aprint_error_dev(self, "couldn't establish power handler\n");
|
2004-07-01 12:34:24 +04:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
cleanupintr:
|
|
|
|
pci_intr_disestablish(pc,sc->sc_ih);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_chip_init(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
/* disable interrupts */
|
|
|
|
WRITE_REG(sc, TXP_IER, 0);
|
|
|
|
WRITE_REG(sc, TXP_IMR,
|
|
|
|
TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_LATCH);
|
|
|
|
|
|
|
|
/* ack all interrupts */
|
|
|
|
WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
|
|
|
|
TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
|
|
|
|
TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
|
|
|
|
|
|
|
|
if (txp_reset_adapter(sc))
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
/* disable interrupts */
|
|
|
|
WRITE_REG(sc, TXP_IER, 0);
|
|
|
|
WRITE_REG(sc, TXP_IMR,
|
|
|
|
TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_LATCH);
|
|
|
|
|
|
|
|
/* ack all interrupts */
|
|
|
|
WRITE_REG(sc, TXP_ISR, TXP_INT_RESERVED | TXP_INT_LATCH |
|
|
|
|
TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
|
|
|
|
TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_A2H_3 | TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_reset_adapter(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
u_int32_t r;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_SRR, TXP_SRR_ALL);
|
|
|
|
DELAY(1000);
|
|
|
|
WRITE_REG(sc, TXP_SRR, 0);
|
|
|
|
|
|
|
|
/* Should wait max 6 seconds */
|
|
|
|
for (i = 0; i < 6000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r == STAT_WAITING_FOR_HOST_REQUEST)
|
|
|
|
break;
|
|
|
|
DELAY(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r != STAT_WAITING_FOR_HOST_REQUEST) {
|
|
|
|
printf("%s: reset hung\n", TXP_DEVNAME(sc));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_download_fw(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
2005-06-18 20:10:44 +04:00
|
|
|
const struct txp_fw_file_header *fileheader;
|
|
|
|
const struct txp_fw_section_header *secthead;
|
2003-07-02 00:08:51 +04:00
|
|
|
int sect;
|
|
|
|
u_int32_t r, i, ier, imr;
|
|
|
|
|
|
|
|
ier = READ_REG(sc, TXP_IER);
|
|
|
|
WRITE_REG(sc, TXP_IER, ier | TXP_INT_A2H_0);
|
|
|
|
|
|
|
|
imr = READ_REG(sc, TXP_IMR);
|
|
|
|
WRITE_REG(sc, TXP_IMR, imr | TXP_INT_A2H_0);
|
|
|
|
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r == STAT_WAITING_FOR_HOST_REQUEST)
|
|
|
|
break;
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
if (r != STAT_WAITING_FOR_HOST_REQUEST) {
|
|
|
|
printf(": not waiting for host request\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ack the status */
|
|
|
|
WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
|
|
|
|
|
2005-06-18 20:10:44 +04:00
|
|
|
fileheader = (const struct txp_fw_file_header *)tc990image;
|
2009-03-18 18:14:29 +03:00
|
|
|
if (memcmp("TYPHOON", fileheader->magicid, sizeof(fileheader->magicid))) {
|
2003-07-02 00:08:51 +04:00
|
|
|
printf(": fw invalid magic\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Tell boot firmware to get ready for image */
|
|
|
|
WRITE_REG(sc, TXP_H2A_1, le32toh(fileheader->addr));
|
|
|
|
WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_RUNTIME_IMAGE);
|
|
|
|
|
|
|
|
if (txp_download_fw_wait(sc)) {
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: fw wait failed, initial\n", device_xname(&sc->sc_dev));
|
2003-07-02 00:08:51 +04:00
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2005-06-18 20:10:44 +04:00
|
|
|
secthead = (const struct txp_fw_section_header *)
|
|
|
|
(((const u_int8_t *)tc990image) +
|
|
|
|
sizeof(struct txp_fw_file_header));
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
for (sect = 0; sect < le32toh(fileheader->nsections); sect++) {
|
|
|
|
if (txp_download_fw_section(sc, secthead, sect))
|
|
|
|
return (-1);
|
2005-06-18 20:10:44 +04:00
|
|
|
secthead = (const struct txp_fw_section_header *)
|
|
|
|
(((const u_int8_t *)secthead) + le32toh(secthead->nbytes) +
|
2003-07-02 00:08:51 +04:00
|
|
|
sizeof(*secthead));
|
|
|
|
}
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_DOWNLOAD_COMPLETE);
|
|
|
|
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r == STAT_WAITING_FOR_BOOT)
|
|
|
|
break;
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
if (r != STAT_WAITING_FOR_BOOT) {
|
|
|
|
printf(": not waiting for boot\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_IER, ier);
|
|
|
|
WRITE_REG(sc, TXP_IMR, imr);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_download_fw_wait(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
u_int32_t i, r;
|
|
|
|
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_ISR);
|
|
|
|
if (r & TXP_INT_A2H_0)
|
|
|
|
break;
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(r & TXP_INT_A2H_0)) {
|
|
|
|
printf(": fw wait failed comm0\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_ISR, TXP_INT_A2H_0);
|
|
|
|
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r != STAT_WAITING_FOR_SEGMENT) {
|
|
|
|
printf(": fw not waiting for segment\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_download_fw_section(struct txp_softc *sc, const struct txp_fw_section_header *sect, int sectnum)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_dma_alloc dma;
|
|
|
|
int rseg, err = 0;
|
|
|
|
struct mbuf m;
|
2005-12-28 12:15:32 +03:00
|
|
|
#ifdef INET
|
2003-07-02 00:08:51 +04:00
|
|
|
u_int16_t csum;
|
2005-12-28 12:15:32 +03:00
|
|
|
#endif
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
/* Skip zero length sections */
|
|
|
|
if (sect->nbytes == 0)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
/* Make sure we aren't past the end of the image */
|
2005-06-18 20:10:44 +04:00
|
|
|
rseg = ((const u_int8_t *)sect) - ((const u_int8_t *)tc990image);
|
2003-07-02 00:08:51 +04:00
|
|
|
if (rseg >= sizeof(tc990image)) {
|
|
|
|
printf(": fw invalid section address, section %d\n", sectnum);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make sure this section doesn't go past the end */
|
|
|
|
rseg += le32toh(sect->nbytes);
|
|
|
|
if (rseg >= sizeof(tc990image)) {
|
|
|
|
printf(": fw truncated section %d\n", sectnum);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* map a buffer, copy segment to it, get physaddr */
|
|
|
|
if (txp_dma_malloc(sc, le32toh(sect->nbytes), &dma, 0)) {
|
|
|
|
printf(": fw dma malloc failed, section %d\n", sectnum);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(dma.dma_vaddr, ((const u_int8_t *)sect) + sizeof(*sect),
|
2003-07-02 00:08:51 +04:00
|
|
|
le32toh(sect->nbytes));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* dummy up mbuf and verify section checksum
|
|
|
|
*/
|
|
|
|
m.m_type = MT_DATA;
|
|
|
|
m.m_next = m.m_nextpkt = NULL;
|
|
|
|
m.m_len = le32toh(sect->nbytes);
|
|
|
|
m.m_data = dma.dma_vaddr;
|
|
|
|
m.m_flags = 0;
|
2005-12-28 12:15:32 +03:00
|
|
|
#ifdef INET
|
2003-07-02 00:08:51 +04:00
|
|
|
csum = in_cksum(&m, le32toh(sect->nbytes));
|
|
|
|
if (csum != sect->cksum) {
|
|
|
|
printf(": fw section %d, bad cksum (expected 0x%x got 0x%x)\n",
|
|
|
|
sectnum, sect->cksum, csum);
|
2005-12-28 12:15:32 +03:00
|
|
|
txp_dma_free(sc, &dma);
|
|
|
|
return -1;
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
2005-12-28 12:15:32 +03:00
|
|
|
#endif
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma.dma_map, 0,
|
|
|
|
dma.dma_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_H2A_1, le32toh(sect->nbytes));
|
|
|
|
WRITE_REG(sc, TXP_H2A_2, le32toh(sect->cksum));
|
|
|
|
WRITE_REG(sc, TXP_H2A_3, le32toh(sect->addr));
|
|
|
|
WRITE_REG(sc, TXP_H2A_4, dma.dma_paddr >> 32);
|
|
|
|
WRITE_REG(sc, TXP_H2A_5, dma.dma_paddr & 0xffffffff);
|
|
|
|
WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_SEGMENT_AVAILABLE);
|
|
|
|
|
|
|
|
if (txp_download_fw_wait(sc)) {
|
|
|
|
printf("%s: fw wait failed, section %d\n",
|
2008-04-10 23:13:36 +04:00
|
|
|
device_xname(&sc->sc_dev), sectnum);
|
2003-07-02 00:08:51 +04:00
|
|
|
err = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma.dma_map, 0,
|
|
|
|
dma.dma_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
|
|
|
txp_dma_free(sc, &dma);
|
|
|
|
return (err);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_intr(void *vsc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = vsc;
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
u_int32_t isr;
|
|
|
|
int claimed = 0;
|
|
|
|
|
|
|
|
/* mask all interrupts */
|
|
|
|
WRITE_REG(sc, TXP_IMR, TXP_INT_RESERVED | TXP_INT_SELF |
|
|
|
|
TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
|
|
|
|
TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | TXP_INT_LATCH);
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_host_dma.dma_map, 0,
|
|
|
|
sizeof(struct txp_hostvar), BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD);
|
|
|
|
|
|
|
|
isr = READ_REG(sc, TXP_ISR);
|
|
|
|
while (isr) {
|
|
|
|
claimed = 1;
|
|
|
|
WRITE_REG(sc, TXP_ISR, isr);
|
|
|
|
|
|
|
|
if ((*sc->sc_rxhir.r_roff) != (*sc->sc_rxhir.r_woff))
|
|
|
|
txp_rx_reclaim(sc, &sc->sc_rxhir, &sc->sc_rxhiring_dma);
|
|
|
|
if ((*sc->sc_rxlor.r_roff) != (*sc->sc_rxlor.r_woff))
|
|
|
|
txp_rx_reclaim(sc, &sc->sc_rxlor, &sc->sc_rxloring_dma);
|
|
|
|
|
|
|
|
if (hv->hv_rx_buf_write_idx == hv->hv_rx_buf_read_idx)
|
|
|
|
txp_rxbuf_reclaim(sc);
|
|
|
|
|
|
|
|
if (sc->sc_txhir.r_cnt && (sc->sc_txhir.r_cons !=
|
|
|
|
TXP_OFFSET2IDX(le32toh(*(sc->sc_txhir.r_off)))))
|
|
|
|
txp_tx_reclaim(sc, &sc->sc_txhir, &sc->sc_txhiring_dma);
|
|
|
|
|
|
|
|
if (sc->sc_txlor.r_cnt && (sc->sc_txlor.r_cons !=
|
|
|
|
TXP_OFFSET2IDX(le32toh(*(sc->sc_txlor.r_off)))))
|
|
|
|
txp_tx_reclaim(sc, &sc->sc_txlor, &sc->sc_txloring_dma);
|
|
|
|
|
|
|
|
isr = READ_REG(sc, TXP_ISR);
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_host_dma.dma_map, 0,
|
|
|
|
sizeof(struct txp_hostvar), BUS_DMASYNC_POSTWRITE|BUS_DMASYNC_POSTREAD);
|
|
|
|
|
|
|
|
/* unmask all interrupts */
|
|
|
|
WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
|
|
|
|
|
|
|
|
txp_start(&sc->sc_arpcom.ec_if);
|
|
|
|
|
|
|
|
return (claimed);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_rx_reclaim(struct txp_softc *sc, struct txp_rx_ring *r, struct txp_dma_alloc *dma)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
struct txp_rx_desc *rxd;
|
|
|
|
struct mbuf *m;
|
|
|
|
struct txp_swdesc *sd;
|
|
|
|
u_int32_t roff, woff;
|
|
|
|
int sumflags = 0;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
roff = le32toh(*r->r_roff);
|
|
|
|
woff = le32toh(*r->r_woff);
|
|
|
|
idx = roff / sizeof(struct txp_rx_desc);
|
|
|
|
rxd = r->r_desc + idx;
|
|
|
|
|
|
|
|
while (roff != woff) {
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma->dma_map,
|
|
|
|
idx * sizeof(struct txp_rx_desc), sizeof(struct txp_rx_desc),
|
|
|
|
BUS_DMASYNC_POSTREAD);
|
|
|
|
|
|
|
|
if (rxd->rx_flags & RX_FLAGS_ERROR) {
|
2008-04-10 23:13:36 +04:00
|
|
|
printf("%s: error 0x%x\n", device_xname(&sc->sc_dev),
|
2003-07-02 00:08:51 +04:00
|
|
|
le32toh(rxd->rx_stat));
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* retrieve stashed pointer */
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(&sd, __UNVOLATILE(&rxd->rx_vaddrlo), sizeof(sd));
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map, 0,
|
|
|
|
sd->sd_map->dm_mapsize, BUS_DMASYNC_POSTREAD);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sd->sd_map);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sd->sd_map);
|
|
|
|
m = sd->sd_mbuf;
|
|
|
|
free(sd, M_DEVBUF);
|
|
|
|
m->m_pkthdr.len = m->m_len = le16toh(rxd->rx_len);
|
|
|
|
|
|
|
|
#ifdef __STRICT_ALIGNMENT
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* XXX Nice chip, except it won't accept "off by 2"
|
|
|
|
* buffers, so we're force to copy. Supposedly
|
|
|
|
* this will be fixed in a newer firmware rev
|
|
|
|
* and this will be temporary.
|
|
|
|
*/
|
|
|
|
struct mbuf *mnew;
|
|
|
|
|
|
|
|
MGETHDR(mnew, M_DONTWAIT, MT_DATA);
|
|
|
|
if (mnew == NULL) {
|
|
|
|
m_freem(m);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
if (m->m_len > (MHLEN - 2)) {
|
|
|
|
MCLGET(mnew, M_DONTWAIT);
|
|
|
|
if (!(mnew->m_flags & M_EXT)) {
|
|
|
|
m_freem(mnew);
|
|
|
|
m_freem(m);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mnew->m_pkthdr.rcvif = ifp;
|
|
|
|
mnew->m_pkthdr.len = mnew->m_len = m->m_len;
|
|
|
|
mnew->m_data += 2;
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(mnew->m_data, m->m_data, m->m_len);
|
2003-07-02 00:08:51 +04:00
|
|
|
m_freem(m);
|
|
|
|
m = mnew;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle BPF listeners. Let the BPF user see the packet.
|
|
|
|
*/
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (rxd->rx_stat & htole32(RX_STAT_IPCKSUMBAD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= (M_CSUM_IPv4|M_CSUM_IPv4_BAD);
|
2003-07-02 00:08:51 +04:00
|
|
|
else if (rxd->rx_stat & htole32(RX_STAT_IPCKSUMGOOD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= M_CSUM_IPv4;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (rxd->rx_stat & htole32(RX_STAT_TCPCKSUMBAD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= (M_CSUM_TCPv4|M_CSUM_TCP_UDP_BAD);
|
2003-07-02 00:08:51 +04:00
|
|
|
else if (rxd->rx_stat & htole32(RX_STAT_TCPCKSUMGOOD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= M_CSUM_TCPv4;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (rxd->rx_stat & htole32(RX_STAT_UDPCKSUMBAD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= (M_CSUM_UDPv4|M_CSUM_TCP_UDP_BAD);
|
2003-07-02 00:08:51 +04:00
|
|
|
else if (rxd->rx_stat & htole32(RX_STAT_UDPCKSUMGOOD))
|
2003-07-07 19:18:24 +04:00
|
|
|
sumflags |= M_CSUM_UDPv4;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
m->m_pkthdr.csum_flags = sumflags;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if (rxd->rx_stat & htole32(RX_STAT_VLAN)) {
|
2005-02-20 18:56:03 +03:00
|
|
|
VLAN_INPUT_TAG(ifp, m, htons(rxd->rx_vlan >> 16),
|
|
|
|
continue);
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
(*ifp->if_input)(ifp, m);
|
|
|
|
|
|
|
|
next:
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma->dma_map,
|
|
|
|
idx * sizeof(struct txp_rx_desc), sizeof(struct txp_rx_desc),
|
|
|
|
BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
roff += sizeof(struct txp_rx_desc);
|
|
|
|
if (roff == (RX_ENTRIES * sizeof(struct txp_rx_desc))) {
|
|
|
|
idx = 0;
|
|
|
|
roff = 0;
|
|
|
|
rxd = r->r_desc;
|
|
|
|
} else {
|
|
|
|
idx++;
|
|
|
|
rxd++;
|
|
|
|
}
|
|
|
|
woff = le32toh(*r->r_woff);
|
|
|
|
}
|
|
|
|
|
|
|
|
*r->r_roff = htole32(woff);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_rxbuf_reclaim(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
struct txp_rxbuf_desc *rbd;
|
|
|
|
struct txp_swdesc *sd;
|
|
|
|
u_int32_t i, end;
|
|
|
|
|
|
|
|
end = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_read_idx));
|
|
|
|
i = TXP_OFFSET2IDX(le32toh(hv->hv_rx_buf_write_idx));
|
|
|
|
|
|
|
|
if (++i == RXBUF_ENTRIES)
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
rbd = sc->sc_rxbufs + i;
|
|
|
|
|
|
|
|
while (i != end) {
|
|
|
|
sd = (struct txp_swdesc *)malloc(sizeof(struct txp_swdesc),
|
|
|
|
M_DEVBUF, M_NOWAIT);
|
|
|
|
if (sd == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA);
|
|
|
|
if (sd->sd_mbuf == NULL)
|
|
|
|
goto err_sd;
|
|
|
|
|
|
|
|
MCLGET(sd->sd_mbuf, M_DONTWAIT);
|
|
|
|
if ((sd->sd_mbuf->m_flags & M_EXT) == 0)
|
|
|
|
goto err_mbuf;
|
|
|
|
sd->sd_mbuf->m_pkthdr.rcvif = ifp;
|
|
|
|
sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, TXP_MAX_PKTLEN, 1,
|
|
|
|
TXP_MAX_PKTLEN, 0, BUS_DMA_NOWAIT, &sd->sd_map))
|
|
|
|
goto err_mbuf;
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, sd->sd_map, sd->sd_mbuf,
|
|
|
|
BUS_DMA_NOWAIT)) {
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sd->sd_map);
|
|
|
|
goto err_mbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_rxbufring_dma.dma_map,
|
|
|
|
i * sizeof(struct txp_rxbuf_desc),
|
|
|
|
sizeof(struct txp_rxbuf_desc), BUS_DMASYNC_POSTWRITE);
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
/* stash away pointer */
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(__UNVOLATILE(&rbd->rb_vaddrlo), &sd, sizeof(sd));
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
rbd->rb_paddrlo = ((u_int64_t)sd->sd_map->dm_segs[0].ds_addr)
|
|
|
|
& 0xffffffff;
|
|
|
|
rbd->rb_paddrhi = ((u_int64_t)sd->sd_map->dm_segs[0].ds_addr)
|
|
|
|
>> 32;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map, 0,
|
|
|
|
sd->sd_map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_rxbufring_dma.dma_map,
|
|
|
|
i * sizeof(struct txp_rxbuf_desc),
|
|
|
|
sizeof(struct txp_rxbuf_desc), BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
hv->hv_rx_buf_write_idx = htole32(TXP_IDX2OFFSET(i));
|
|
|
|
|
|
|
|
if (++i == RXBUF_ENTRIES) {
|
|
|
|
i = 0;
|
|
|
|
rbd = sc->sc_rxbufs;
|
|
|
|
} else
|
|
|
|
rbd++;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
err_mbuf:
|
|
|
|
m_freem(sd->sd_mbuf);
|
|
|
|
err_sd:
|
|
|
|
free(sd, M_DEVBUF);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reclaim mbufs and entries from a transmit ring.
|
|
|
|
*/
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_tx_reclaim(struct txp_softc *sc, struct txp_tx_ring *r, struct txp_dma_alloc *dma)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
u_int32_t idx = TXP_OFFSET2IDX(le32toh(*(r->r_off)));
|
|
|
|
u_int32_t cons = r->r_cons, cnt = r->r_cnt;
|
|
|
|
struct txp_tx_desc *txd = r->r_desc + cons;
|
|
|
|
struct txp_swdesc *sd = sc->sc_txd + cons;
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
while (cons != idx) {
|
|
|
|
if (cnt == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, dma->dma_map,
|
|
|
|
cons * sizeof(struct txp_tx_desc),
|
|
|
|
sizeof(struct txp_tx_desc),
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
|
|
|
if ((txd->tx_flags & TX_FLAGS_TYPE_M) ==
|
|
|
|
TX_FLAGS_TYPE_DATA) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map, 0,
|
|
|
|
sd->sd_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sd->sd_map);
|
|
|
|
m = sd->sd_mbuf;
|
|
|
|
if (m != NULL) {
|
|
|
|
m_freem(m);
|
|
|
|
txd->tx_addrlo = 0;
|
|
|
|
txd->tx_addrhi = 0;
|
|
|
|
ifp->if_opackets++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
|
|
|
|
if (++cons == TX_ENTRIES) {
|
|
|
|
txd = r->r_desc;
|
|
|
|
cons = 0;
|
|
|
|
sd = sc->sc_txd;
|
|
|
|
} else {
|
|
|
|
txd++;
|
|
|
|
sd++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cnt--;
|
|
|
|
}
|
|
|
|
|
|
|
|
r->r_cons = cons;
|
|
|
|
r->r_cnt = cnt;
|
|
|
|
if (cnt == 0)
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
}
|
|
|
|
|
2009-09-27 16:52:59 +04:00
|
|
|
bool
|
|
|
|
txp_shutdown(device_t self, int howto)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
2009-09-27 16:52:59 +04:00
|
|
|
struct txp_softc *sc;
|
|
|
|
|
|
|
|
sc = device_private(self);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
/* mask all interrupts */
|
|
|
|
WRITE_REG(sc, TXP_IMR,
|
|
|
|
TXP_INT_SELF | TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_LATCH);
|
|
|
|
|
|
|
|
txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
|
|
|
|
txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 0);
|
|
|
|
txp_command(sc, TXP_CMD_HALT, 0, 0, 0, NULL, NULL, NULL, 0);
|
2009-09-27 16:52:59 +04:00
|
|
|
|
|
|
|
return true;
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_alloc_rings(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
struct txp_boot_record *boot;
|
|
|
|
struct txp_swdesc *sd;
|
|
|
|
u_int32_t r;
|
2006-04-14 22:45:53 +04:00
|
|
|
int i, j, nb;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
/* boot record */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_boot_record), &sc->sc_boot_dma,
|
|
|
|
BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate boot record\n");
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
boot = (struct txp_boot_record *)sc->sc_boot_dma.dma_vaddr;
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(boot, 0, sizeof(*boot));
|
2003-07-02 00:08:51 +04:00
|
|
|
sc->sc_boot = boot;
|
|
|
|
|
|
|
|
/* host variables */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_hostvar), &sc->sc_host_dma,
|
|
|
|
BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate host ring\n");
|
|
|
|
goto bail_boot;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_host_dma.dma_vaddr, 0, sizeof(struct txp_hostvar));
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_hostvar_lo = htole32(sc->sc_host_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_hostvar_hi = htole32(sc->sc_host_dma.dma_paddr >> 32);
|
|
|
|
sc->sc_hostvar = (struct txp_hostvar *)sc->sc_host_dma.dma_vaddr;
|
|
|
|
|
|
|
|
/* high priority tx ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_tx_desc) * TX_ENTRIES,
|
|
|
|
&sc->sc_txhiring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate high tx ring\n");
|
|
|
|
goto bail_host;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_txhiring_dma.dma_vaddr, 0, sizeof(struct txp_tx_desc) * TX_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_txhipri_lo = htole32(sc->sc_txhiring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_txhipri_hi = htole32(sc->sc_txhiring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_txhipri_siz = htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
|
|
|
|
sc->sc_txhir.r_reg = TXP_H2A_1;
|
|
|
|
sc->sc_txhir.r_desc = (struct txp_tx_desc *)sc->sc_txhiring_dma.dma_vaddr;
|
|
|
|
sc->sc_txhir.r_cons = sc->sc_txhir.r_prod = sc->sc_txhir.r_cnt = 0;
|
|
|
|
sc->sc_txhir.r_off = &sc->sc_hostvar->hv_tx_hi_desc_read_idx;
|
|
|
|
for (i = 0; i < TX_ENTRIES; i++) {
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, TXP_MAX_PKTLEN,
|
|
|
|
TX_ENTRIES - 4, TXP_MAX_SEGLEN, 0,
|
|
|
|
BUS_DMA_NOWAIT, &sc->sc_txd[i].sd_map) != 0) {
|
|
|
|
for (j = 0; j < i; j++) {
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat,
|
|
|
|
sc->sc_txd[j].sd_map);
|
|
|
|
sc->sc_txd[j].sd_map = NULL;
|
|
|
|
}
|
|
|
|
goto bail_txhiring;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* low priority tx ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_tx_desc) * TX_ENTRIES,
|
|
|
|
&sc->sc_txloring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate low tx ring\n");
|
|
|
|
goto bail_txhiring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_txloring_dma.dma_vaddr, 0, sizeof(struct txp_tx_desc) * TX_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_txlopri_lo = htole32(sc->sc_txloring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_txlopri_hi = htole32(sc->sc_txloring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_txlopri_siz = htole32(TX_ENTRIES * sizeof(struct txp_tx_desc));
|
|
|
|
sc->sc_txlor.r_reg = TXP_H2A_3;
|
|
|
|
sc->sc_txlor.r_desc = (struct txp_tx_desc *)sc->sc_txloring_dma.dma_vaddr;
|
|
|
|
sc->sc_txlor.r_cons = sc->sc_txlor.r_prod = sc->sc_txlor.r_cnt = 0;
|
|
|
|
sc->sc_txlor.r_off = &sc->sc_hostvar->hv_tx_lo_desc_read_idx;
|
|
|
|
|
|
|
|
/* high priority rx ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_rx_desc) * RX_ENTRIES,
|
|
|
|
&sc->sc_rxhiring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate high rx ring\n");
|
|
|
|
goto bail_txloring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_rxhiring_dma.dma_vaddr, 0, sizeof(struct txp_rx_desc) * RX_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_rxhipri_lo = htole32(sc->sc_rxhiring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_rxhipri_hi = htole32(sc->sc_rxhiring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_rxhipri_siz = htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
|
|
|
|
sc->sc_rxhir.r_desc =
|
|
|
|
(struct txp_rx_desc *)sc->sc_rxhiring_dma.dma_vaddr;
|
|
|
|
sc->sc_rxhir.r_roff = &sc->sc_hostvar->hv_rx_hi_read_idx;
|
|
|
|
sc->sc_rxhir.r_woff = &sc->sc_hostvar->hv_rx_hi_write_idx;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_rxhiring_dma.dma_map,
|
|
|
|
0, sc->sc_rxhiring_dma.dma_map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
/* low priority ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_rx_desc) * RX_ENTRIES,
|
|
|
|
&sc->sc_rxloring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate low rx ring\n");
|
|
|
|
goto bail_rxhiring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_rxloring_dma.dma_vaddr, 0, sizeof(struct txp_rx_desc) * RX_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_rxlopri_lo = htole32(sc->sc_rxloring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_rxlopri_hi = htole32(sc->sc_rxloring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_rxlopri_siz = htole32(RX_ENTRIES * sizeof(struct txp_rx_desc));
|
|
|
|
sc->sc_rxlor.r_desc =
|
|
|
|
(struct txp_rx_desc *)sc->sc_rxloring_dma.dma_vaddr;
|
|
|
|
sc->sc_rxlor.r_roff = &sc->sc_hostvar->hv_rx_lo_read_idx;
|
|
|
|
sc->sc_rxlor.r_woff = &sc->sc_hostvar->hv_rx_lo_write_idx;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_rxloring_dma.dma_map,
|
|
|
|
0, sc->sc_rxloring_dma.dma_map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
/* command ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_cmd_desc) * CMD_ENTRIES,
|
|
|
|
&sc->sc_cmdring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate command ring\n");
|
|
|
|
goto bail_rxloring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_cmdring_dma.dma_vaddr, 0, sizeof(struct txp_cmd_desc) * CMD_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_cmd_lo = htole32(sc->sc_cmdring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_cmd_hi = htole32(sc->sc_cmdring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_cmd_siz = htole32(CMD_ENTRIES * sizeof(struct txp_cmd_desc));
|
|
|
|
sc->sc_cmdring.base = (struct txp_cmd_desc *)sc->sc_cmdring_dma.dma_vaddr;
|
|
|
|
sc->sc_cmdring.size = CMD_ENTRIES * sizeof(struct txp_cmd_desc);
|
|
|
|
sc->sc_cmdring.lastwrite = 0;
|
|
|
|
|
|
|
|
/* response ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_rsp_desc) * RSP_ENTRIES,
|
|
|
|
&sc->sc_rspring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate response ring\n");
|
|
|
|
goto bail_cmdring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_rspring_dma.dma_vaddr, 0, sizeof(struct txp_rsp_desc) * RSP_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_resp_lo = htole32(sc->sc_rspring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_resp_hi = htole32(sc->sc_rspring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_resp_siz = htole32(CMD_ENTRIES * sizeof(struct txp_rsp_desc));
|
|
|
|
sc->sc_rspring.base = (struct txp_rsp_desc *)sc->sc_rspring_dma.dma_vaddr;
|
|
|
|
sc->sc_rspring.size = RSP_ENTRIES * sizeof(struct txp_rsp_desc);
|
|
|
|
sc->sc_rspring.lastwrite = 0;
|
|
|
|
|
|
|
|
/* receive buffer ring */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES,
|
|
|
|
&sc->sc_rxbufring_dma, BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate rx buffer ring\n");
|
|
|
|
goto bail_rspring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_rxbufring_dma.dma_vaddr, 0, sizeof(struct txp_rxbuf_desc) * RXBUF_ENTRIES);
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_rxbuf_lo = htole32(sc->sc_rxbufring_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_rxbuf_hi = htole32(sc->sc_rxbufring_dma.dma_paddr >> 32);
|
|
|
|
boot->br_rxbuf_siz = htole32(RXBUF_ENTRIES * sizeof(struct txp_rxbuf_desc));
|
|
|
|
sc->sc_rxbufs = (struct txp_rxbuf_desc *)sc->sc_rxbufring_dma.dma_vaddr;
|
2006-04-14 22:45:53 +04:00
|
|
|
for (nb = 0; nb < RXBUF_ENTRIES; nb++) {
|
2003-07-02 00:08:51 +04:00
|
|
|
sd = (struct txp_swdesc *)malloc(sizeof(struct txp_swdesc),
|
|
|
|
M_DEVBUF, M_NOWAIT);
|
2006-04-14 22:45:53 +04:00
|
|
|
/* stash away pointer */
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(__UNVOLATILE(&sc->sc_rxbufs[nb].rb_vaddrlo), &sd, sizeof(sd));
|
2003-07-02 00:08:51 +04:00
|
|
|
if (sd == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
MGETHDR(sd->sd_mbuf, M_DONTWAIT, MT_DATA);
|
|
|
|
if (sd->sd_mbuf == NULL) {
|
|
|
|
goto bail_rxbufring;
|
|
|
|
}
|
|
|
|
|
|
|
|
MCLGET(sd->sd_mbuf, M_DONTWAIT);
|
|
|
|
if ((sd->sd_mbuf->m_flags & M_EXT) == 0) {
|
|
|
|
goto bail_rxbufring;
|
|
|
|
}
|
|
|
|
sd->sd_mbuf->m_pkthdr.len = sd->sd_mbuf->m_len = MCLBYTES;
|
|
|
|
sd->sd_mbuf->m_pkthdr.rcvif = ifp;
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, TXP_MAX_PKTLEN, 1,
|
|
|
|
TXP_MAX_PKTLEN, 0, BUS_DMA_NOWAIT, &sd->sd_map)) {
|
|
|
|
goto bail_rxbufring;
|
|
|
|
}
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, sd->sd_map, sd->sd_mbuf,
|
|
|
|
BUS_DMA_NOWAIT)) {
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, sd->sd_map);
|
|
|
|
goto bail_rxbufring;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map, 0,
|
|
|
|
sd->sd_map->dm_mapsize, BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
|
2006-04-14 22:45:53 +04:00
|
|
|
sc->sc_rxbufs[nb].rb_paddrlo =
|
2003-07-02 00:08:51 +04:00
|
|
|
((u_int64_t)sd->sd_map->dm_segs[0].ds_addr) & 0xffffffff;
|
2006-04-14 22:45:53 +04:00
|
|
|
sc->sc_rxbufs[nb].rb_paddrhi =
|
2003-07-02 00:08:51 +04:00
|
|
|
((u_int64_t)sd->sd_map->dm_segs[0].ds_addr) >> 32;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_rxbufring_dma.dma_map,
|
|
|
|
0, sc->sc_rxbufring_dma.dma_map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
sc->sc_hostvar->hv_rx_buf_write_idx = htole32((RXBUF_ENTRIES - 1) *
|
|
|
|
sizeof(struct txp_rxbuf_desc));
|
|
|
|
|
|
|
|
/* zero dma */
|
|
|
|
if (txp_dma_malloc(sc, sizeof(u_int32_t), &sc->sc_zero_dma,
|
|
|
|
BUS_DMA_COHERENT)) {
|
|
|
|
printf(": can't allocate response ring\n");
|
|
|
|
goto bail_rxbufring;
|
|
|
|
}
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(sc->sc_zero_dma.dma_vaddr, 0, sizeof(u_int32_t));
|
2003-07-02 00:08:51 +04:00
|
|
|
boot->br_zero_lo = htole32(sc->sc_zero_dma.dma_paddr & 0xffffffff);
|
|
|
|
boot->br_zero_hi = htole32(sc->sc_zero_dma.dma_paddr >> 32);
|
|
|
|
|
|
|
|
/* See if it's waiting for boot, and try to boot it */
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r == STAT_WAITING_FOR_BOOT)
|
|
|
|
break;
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
if (r != STAT_WAITING_FOR_BOOT) {
|
|
|
|
printf(": not waiting for boot\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
WRITE_REG(sc, TXP_H2A_2, sc->sc_boot_dma.dma_paddr >> 32);
|
|
|
|
WRITE_REG(sc, TXP_H2A_1, sc->sc_boot_dma.dma_paddr & 0xffffffff);
|
|
|
|
WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_REGISTER_BOOT_RECORD);
|
|
|
|
|
|
|
|
/* See if it booted */
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
r = READ_REG(sc, TXP_A2H_0);
|
|
|
|
if (r == STAT_RUNNING)
|
|
|
|
break;
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
if (r != STAT_RUNNING) {
|
|
|
|
printf(": fw not running\n");
|
|
|
|
goto bail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear TX and CMD ring write registers */
|
|
|
|
WRITE_REG(sc, TXP_H2A_1, TXP_BOOTCMD_NULL);
|
|
|
|
WRITE_REG(sc, TXP_H2A_2, TXP_BOOTCMD_NULL);
|
|
|
|
WRITE_REG(sc, TXP_H2A_3, TXP_BOOTCMD_NULL);
|
|
|
|
WRITE_REG(sc, TXP_H2A_0, TXP_BOOTCMD_NULL);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
bail:
|
|
|
|
txp_dma_free(sc, &sc->sc_zero_dma);
|
|
|
|
bail_rxbufring:
|
2006-04-14 22:45:53 +04:00
|
|
|
if (nb == RXBUF_ENTRIES)
|
|
|
|
nb--;
|
|
|
|
for (i = 0; i <= nb; i++) {
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(&sd, __UNVOLATILE(&sc->sc_rxbufs[i].rb_vaddrlo),
|
2006-04-14 22:45:53 +04:00
|
|
|
sizeof(sd));
|
|
|
|
if (sd)
|
|
|
|
free(sd, M_DEVBUF);
|
|
|
|
}
|
2003-07-02 00:08:51 +04:00
|
|
|
txp_dma_free(sc, &sc->sc_rxbufring_dma);
|
|
|
|
bail_rspring:
|
|
|
|
txp_dma_free(sc, &sc->sc_rspring_dma);
|
|
|
|
bail_cmdring:
|
|
|
|
txp_dma_free(sc, &sc->sc_cmdring_dma);
|
|
|
|
bail_rxloring:
|
|
|
|
txp_dma_free(sc, &sc->sc_rxloring_dma);
|
|
|
|
bail_rxhiring:
|
|
|
|
txp_dma_free(sc, &sc->sc_rxhiring_dma);
|
|
|
|
bail_txloring:
|
|
|
|
txp_dma_free(sc, &sc->sc_txloring_dma);
|
|
|
|
bail_txhiring:
|
|
|
|
txp_dma_free(sc, &sc->sc_txhiring_dma);
|
|
|
|
bail_host:
|
|
|
|
txp_dma_free(sc, &sc->sc_host_dma);
|
|
|
|
bail_boot:
|
|
|
|
txp_dma_free(sc, &sc->sc_boot_dma);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_dma_malloc(struct txp_softc *sc, bus_size_t size, struct txp_dma_alloc *dma, int mapflags)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if ((r = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0,
|
|
|
|
&dma->dma_seg, 1, &dma->dma_nseg, 0)) != 0)
|
|
|
|
goto fail_0;
|
|
|
|
|
|
|
|
if ((r = bus_dmamem_map(sc->sc_dmat, &dma->dma_seg, dma->dma_nseg,
|
|
|
|
size, &dma->dma_vaddr, mapflags | BUS_DMA_NOWAIT)) != 0)
|
|
|
|
goto fail_1;
|
|
|
|
|
|
|
|
if ((r = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
|
|
|
|
BUS_DMA_NOWAIT, &dma->dma_map)) != 0)
|
|
|
|
goto fail_2;
|
|
|
|
|
|
|
|
if ((r = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_vaddr,
|
|
|
|
size, NULL, BUS_DMA_NOWAIT)) != 0)
|
|
|
|
goto fail_3;
|
|
|
|
|
|
|
|
dma->dma_paddr = dma->dma_map->dm_segs[0].ds_addr;
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
fail_3:
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
|
|
|
|
fail_2:
|
|
|
|
bus_dmamem_unmap(sc->sc_dmat, dma->dma_vaddr, size);
|
|
|
|
fail_1:
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &dma->dma_seg, dma->dma_nseg);
|
|
|
|
fail_0:
|
|
|
|
return (r);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_dma_free(struct txp_softc *sc, struct txp_dma_alloc *dma)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
|
|
|
|
bus_dmamem_unmap(sc->sc_dmat, dma->dma_vaddr, dma->dma_map->dm_mapsize);
|
|
|
|
bus_dmamem_free(sc->sc_dmat, &dma->dma_seg, dma->dma_nseg);
|
|
|
|
bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
*** 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
|
|
|
txp_ioctl(struct ifnet *ifp, u_long command, void *data)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = ifp->if_softc;
|
|
|
|
struct ifreq *ifr = (struct ifreq *)data;
|
|
|
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
|
|
|
int s, error = 0;
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if ((error = ether_ioctl(ifp, &sc->sc_arpcom, command, data)) > 0) {
|
|
|
|
splx(s);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
switch(command) {
|
*** 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:
|
2003-07-02 00:08:51 +04:00
|
|
|
ifp->if_flags |= IFF_UP;
|
*** 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
|
|
|
txp_init(sc);
|
2003-07-02 00:08:51 +04:00
|
|
|
switch (ifa->ifa_addr->sa_family) {
|
|
|
|
#ifdef INET
|
|
|
|
case AF_INET:
|
|
|
|
arp_ifinit(ifp, ifa);
|
|
|
|
break;
|
|
|
|
#endif /* INET */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIOCSIFFLAGS:
|
*** 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
|
|
|
if ((error = ifioctl_common(ifp, command, data)) != 0)
|
|
|
|
break;
|
2003-07-02 00:08:51 +04:00
|
|
|
if (ifp->if_flags & IFF_UP) {
|
|
|
|
txp_init(sc);
|
|
|
|
} else {
|
|
|
|
if (ifp->if_flags & IFF_RUNNING)
|
|
|
|
txp_stop(sc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
2008-02-07 04:21:52 +03:00
|
|
|
if ((error = ether_ioctl(ifp, command, data)) != ENETRESET)
|
|
|
|
break;
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
|
|
|
if (command != SIOCADDMULTI && command != SIOCDELMULTI)
|
|
|
|
;
|
|
|
|
else if (ifp->if_flags & IFF_RUNNING) {
|
2003-07-02 00:08:51 +04:00
|
|
|
/*
|
|
|
|
* Multicast list has changed; set the hardware
|
|
|
|
* filter accordingly.
|
|
|
|
*/
|
2008-02-07 04:21:52 +03:00
|
|
|
txp_set_filter(sc);
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
|
|
case SIOCSIFMEDIA:
|
|
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->sc_ifmedia, command);
|
|
|
|
break;
|
|
|
|
default:
|
*** 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
|
|
|
error = ether_ioctl(ifp, command, data);
|
2003-07-02 00:08:51 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
|
|
|
|
return(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_init(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
txp_stop(sc);
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
|
|
|
|
txp_set_filter(sc);
|
|
|
|
|
|
|
|
txp_command(sc, TXP_CMD_TX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
|
|
|
|
txp_command(sc, TXP_CMD_RX_ENABLE, 0, 0, 0, NULL, NULL, NULL, 1);
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_IER, TXP_INT_RESERVED | TXP_INT_SELF |
|
|
|
|
TXP_INT_A2H_7 | TXP_INT_A2H_6 | TXP_INT_A2H_5 | TXP_INT_A2H_4 |
|
|
|
|
TXP_INT_A2H_2 | TXP_INT_A2H_1 | TXP_INT_A2H_0 |
|
|
|
|
TXP_INT_DMA3 | TXP_INT_DMA2 | TXP_INT_DMA1 | TXP_INT_DMA0 |
|
|
|
|
TXP_INT_PCI_TABORT | TXP_INT_PCI_MABORT | TXP_INT_LATCH);
|
|
|
|
WRITE_REG(sc, TXP_IMR, TXP_INT_A2H_3);
|
|
|
|
|
|
|
|
ifp->if_flags |= IFF_RUNNING;
|
|
|
|
ifp->if_flags &= ~IFF_OACTIVE;
|
|
|
|
ifp->if_timer = 0;
|
|
|
|
|
|
|
|
if (!callout_pending(&sc->sc_tick))
|
|
|
|
callout_schedule(&sc->sc_tick, hz);
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_tick(void *vsc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = vsc;
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
struct txp_rsp_desc *rsp = NULL;
|
|
|
|
struct txp_ext_desc *ext;
|
|
|
|
int s;
|
|
|
|
|
|
|
|
s = splnet();
|
|
|
|
txp_rxbuf_reclaim(sc);
|
|
|
|
|
|
|
|
if (txp_command2(sc, TXP_CMD_READ_STATISTICS, 0, 0, 0, NULL, 0,
|
|
|
|
&rsp, 1))
|
|
|
|
goto out;
|
|
|
|
if (rsp->rsp_numdesc != 6)
|
|
|
|
goto out;
|
|
|
|
if (txp_command(sc, TXP_CMD_CLEAR_STATISTICS, 0, 0, 0,
|
|
|
|
NULL, NULL, NULL, 1))
|
|
|
|
goto out;
|
|
|
|
ext = (struct txp_ext_desc *)(rsp + 1);
|
|
|
|
|
|
|
|
ifp->if_ierrors += ext[3].ext_2 + ext[3].ext_3 + ext[3].ext_4 +
|
|
|
|
ext[4].ext_1 + ext[4].ext_4;
|
|
|
|
ifp->if_oerrors += ext[0].ext_1 + ext[1].ext_1 + ext[1].ext_4 +
|
|
|
|
ext[2].ext_1;
|
|
|
|
ifp->if_collisions += ext[0].ext_2 + ext[0].ext_3 + ext[1].ext_2 +
|
|
|
|
ext[1].ext_3;
|
|
|
|
ifp->if_opackets += rsp->rsp_par2;
|
|
|
|
ifp->if_ipackets += ext[2].ext_3;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (rsp != NULL)
|
|
|
|
free(rsp, M_DEVBUF);
|
|
|
|
|
|
|
|
splx(s);
|
|
|
|
callout_schedule(&sc->sc_tick, hz);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_start(struct ifnet *ifp)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = ifp->if_softc;
|
|
|
|
struct txp_tx_ring *r = &sc->sc_txhir;
|
|
|
|
struct txp_tx_desc *txd;
|
|
|
|
int txdidx;
|
|
|
|
struct txp_frag_desc *fxd;
|
|
|
|
struct mbuf *m, *mnew;
|
|
|
|
struct txp_swdesc *sd;
|
|
|
|
u_int32_t firstprod, firstcnt, prod, cnt, i;
|
2003-08-20 21:41:38 +04:00
|
|
|
struct m_tag *mtag;
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
|
|
|
|
return;
|
|
|
|
|
|
|
|
prod = r->r_prod;
|
|
|
|
cnt = r->r_cnt;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
IFQ_POLL(&ifp->if_snd, m);
|
|
|
|
if (m == NULL)
|
|
|
|
break;
|
|
|
|
mnew = NULL;
|
|
|
|
|
|
|
|
firstprod = prod;
|
|
|
|
firstcnt = cnt;
|
|
|
|
|
|
|
|
sd = sc->sc_txd + prod;
|
|
|
|
sd->sd_mbuf = m;
|
|
|
|
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, sd->sd_map, m,
|
|
|
|
BUS_DMA_NOWAIT)) {
|
|
|
|
MGETHDR(mnew, M_DONTWAIT, MT_DATA);
|
|
|
|
if (mnew == NULL)
|
|
|
|
goto oactive1;
|
|
|
|
if (m->m_pkthdr.len > MHLEN) {
|
|
|
|
MCLGET(mnew, M_DONTWAIT);
|
|
|
|
if ((mnew->m_flags & M_EXT) == 0) {
|
|
|
|
m_freem(mnew);
|
|
|
|
goto oactive1;
|
|
|
|
}
|
|
|
|
}
|
2007-03-04 08:59:00 +03:00
|
|
|
m_copydata(m, 0, m->m_pkthdr.len, mtod(mnew, void *));
|
2003-07-02 00:08:51 +04:00
|
|
|
mnew->m_pkthdr.len = mnew->m_len = m->m_pkthdr.len;
|
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m);
|
|
|
|
m_freem(m);
|
|
|
|
m = mnew;
|
|
|
|
if (bus_dmamap_load_mbuf(sc->sc_dmat, sd->sd_map, m,
|
|
|
|
BUS_DMA_NOWAIT))
|
|
|
|
goto oactive1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((TX_ENTRIES - cnt) < 4)
|
|
|
|
goto oactive;
|
|
|
|
|
|
|
|
txd = r->r_desc + prod;
|
|
|
|
txdidx = prod;
|
|
|
|
txd->tx_flags = TX_FLAGS_TYPE_DATA;
|
|
|
|
txd->tx_numdesc = 0;
|
|
|
|
txd->tx_addrlo = 0;
|
|
|
|
txd->tx_addrhi = 0;
|
|
|
|
txd->tx_totlen = m->m_pkthdr.len;
|
|
|
|
txd->tx_pflags = 0;
|
|
|
|
txd->tx_numdesc = sd->sd_map->dm_nsegs;
|
|
|
|
|
|
|
|
if (++prod == TX_ENTRIES)
|
|
|
|
prod = 0;
|
|
|
|
|
|
|
|
if (++cnt >= (TX_ENTRIES - 4))
|
|
|
|
goto oactive;
|
|
|
|
|
2005-06-18 20:10:44 +04:00
|
|
|
if ((mtag = VLAN_OUTPUT_TAG(&sc->sc_arpcom, m)))
|
2003-07-02 00:08:51 +04:00
|
|
|
txd->tx_pflags = TX_PFLAGS_VLAN |
|
2005-02-20 18:56:03 +03:00
|
|
|
(htons(VLAN_TAG_VALUE(mtag)) << TX_PFLAGS_VLANTAG_S);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
if (m->m_pkthdr.csum_flags & M_CSUM_IPv4)
|
2003-07-02 00:08:51 +04:00
|
|
|
txd->tx_pflags |= TX_PFLAGS_IPCKSUM;
|
|
|
|
#ifdef TRY_TX_TCP_CSUM
|
2003-07-07 19:18:24 +04:00
|
|
|
if (m->m_pkthdr.csum_flags & M_CSUM_TCPv4)
|
2003-07-02 00:08:51 +04:00
|
|
|
txd->tx_pflags |= TX_PFLAGS_TCPCKSUM;
|
|
|
|
#endif
|
|
|
|
#ifdef TRY_TX_UDP_CSUM
|
2003-07-07 19:18:24 +04:00
|
|
|
if (m->m_pkthdr.csum_flags & M_CSUM_UDPv4)
|
2003-07-02 00:08:51 +04:00
|
|
|
txd->tx_pflags |= TX_PFLAGS_UDPCKSUM;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map, 0,
|
|
|
|
sd->sd_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
fxd = (struct txp_frag_desc *)(r->r_desc + prod);
|
|
|
|
for (i = 0; i < sd->sd_map->dm_nsegs; i++) {
|
|
|
|
if (++cnt >= (TX_ENTRIES - 4)) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sd->sd_map,
|
|
|
|
0, sd->sd_map->dm_mapsize,
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
goto oactive;
|
|
|
|
}
|
|
|
|
|
|
|
|
fxd->frag_flags = FRAG_FLAGS_TYPE_FRAG |
|
|
|
|
FRAG_FLAGS_VALID;
|
|
|
|
fxd->frag_rsvd1 = 0;
|
|
|
|
fxd->frag_len = sd->sd_map->dm_segs[i].ds_len;
|
|
|
|
fxd->frag_addrlo =
|
|
|
|
((u_int64_t)sd->sd_map->dm_segs[i].ds_addr) &
|
|
|
|
0xffffffff;
|
|
|
|
fxd->frag_addrhi =
|
|
|
|
((u_int64_t)sd->sd_map->dm_segs[i].ds_addr) >>
|
|
|
|
32;
|
|
|
|
fxd->frag_rsvd2 = 0;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->sc_dmat,
|
|
|
|
sc->sc_txhiring_dma.dma_map,
|
|
|
|
prod * sizeof(struct txp_frag_desc),
|
|
|
|
sizeof(struct txp_frag_desc), BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
if (++prod == TX_ENTRIES) {
|
|
|
|
fxd = (struct txp_frag_desc *)r->r_desc;
|
|
|
|
prod = 0;
|
|
|
|
} else
|
|
|
|
fxd++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if mnew isn't NULL, we already dequeued and copied
|
|
|
|
* the packet.
|
|
|
|
*/
|
|
|
|
if (mnew == NULL)
|
|
|
|
IFQ_DEQUEUE(&ifp->if_snd, m);
|
|
|
|
|
|
|
|
ifp->if_timer = 5;
|
|
|
|
|
2010-04-05 11:19:28 +04:00
|
|
|
bpf_mtap(ifp, m);
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
txd->tx_flags |= TX_FLAGS_VALID;
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_txhiring_dma.dma_map,
|
|
|
|
txdidx * sizeof(struct txp_tx_desc),
|
|
|
|
sizeof(struct txp_tx_desc), BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
{
|
|
|
|
struct mbuf *mx;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("txd: flags 0x%x ndesc %d totlen %d pflags 0x%x\n",
|
|
|
|
txd->tx_flags, txd->tx_numdesc, txd->tx_totlen,
|
|
|
|
txd->tx_pflags);
|
|
|
|
for (mx = m; mx != NULL; mx = mx->m_next) {
|
|
|
|
for (i = 0; i < mx->m_len; i++) {
|
|
|
|
printf(":%02x",
|
|
|
|
(u_int8_t)m->m_data[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
WRITE_REG(sc, r->r_reg, TXP_IDX2OFFSET(prod));
|
|
|
|
}
|
|
|
|
|
|
|
|
r->r_prod = prod;
|
|
|
|
r->r_cnt = cnt;
|
|
|
|
return;
|
|
|
|
|
|
|
|
oactive:
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sd->sd_map);
|
|
|
|
oactive1:
|
|
|
|
ifp->if_flags |= IFF_OACTIVE;
|
|
|
|
r->r_prod = firstprod;
|
|
|
|
r->r_cnt = firstcnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle simple commands sent to the typhoon
|
|
|
|
*/
|
|
|
|
int
|
2009-03-15 00:04:01 +03:00
|
|
|
txp_command(struct txp_softc *sc, u_int16_t id, u_int16_t in1, u_int32_t in2, u_int32_t in3, u_int16_t *out1, u_int32_t *out2, u_int32_t *out3, int wait)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_rsp_desc *rsp = NULL;
|
|
|
|
|
|
|
|
if (txp_command2(sc, id, in1, in2, in3, NULL, 0, &rsp, wait))
|
|
|
|
return (-1);
|
|
|
|
|
|
|
|
if (!wait)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
if (out1 != NULL)
|
|
|
|
*out1 = le16toh(rsp->rsp_par1);
|
|
|
|
if (out2 != NULL)
|
|
|
|
*out2 = le32toh(rsp->rsp_par2);
|
|
|
|
if (out3 != NULL)
|
|
|
|
*out3 = le32toh(rsp->rsp_par3);
|
|
|
|
free(rsp, M_DEVBUF);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-15 00:04:01 +03:00
|
|
|
txp_command2(struct txp_softc *sc, u_int16_t id, u_int16_t in1, u_int32_t in2, u_int32_t in3, struct txp_ext_desc *in_extp, u_int8_t in_extn, struct txp_rsp_desc **rspp, int wait)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
struct txp_cmd_desc *cmd;
|
|
|
|
struct txp_ext_desc *ext;
|
|
|
|
u_int32_t idx, i;
|
|
|
|
u_int16_t seq;
|
|
|
|
|
|
|
|
if (txp_cmd_desc_numfree(sc) < (in_extn + 1)) {
|
|
|
|
printf("%s: no free cmd descriptors\n", TXP_DEVNAME(sc));
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = sc->sc_cmdring.lastwrite;
|
|
|
|
cmd = (struct txp_cmd_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
|
2009-03-18 19:00:08 +03:00
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
2003-07-02 00:08:51 +04:00
|
|
|
|
|
|
|
cmd->cmd_numdesc = in_extn;
|
|
|
|
seq = sc->sc_seq++;
|
|
|
|
cmd->cmd_seq = htole16(seq);
|
|
|
|
cmd->cmd_id = htole16(id);
|
|
|
|
cmd->cmd_par1 = htole16(in1);
|
|
|
|
cmd->cmd_par2 = htole32(in2);
|
|
|
|
cmd->cmd_par3 = htole32(in3);
|
|
|
|
cmd->cmd_flags = CMD_FLAGS_TYPE_CMD |
|
|
|
|
(wait ? CMD_FLAGS_RESP : 0) | CMD_FLAGS_VALID;
|
|
|
|
|
|
|
|
idx += sizeof(struct txp_cmd_desc);
|
|
|
|
if (idx == sc->sc_cmdring.size)
|
|
|
|
idx = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < in_extn; i++) {
|
|
|
|
ext = (struct txp_ext_desc *)(((u_int8_t *)sc->sc_cmdring.base) + idx);
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(ext, in_extp, sizeof(struct txp_ext_desc));
|
2003-07-02 00:08:51 +04:00
|
|
|
in_extp++;
|
|
|
|
idx += sizeof(struct txp_cmd_desc);
|
|
|
|
if (idx == sc->sc_cmdring.size)
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->sc_cmdring.lastwrite = idx;
|
|
|
|
|
|
|
|
WRITE_REG(sc, TXP_H2A_2, sc->sc_cmdring.lastwrite);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_host_dma.dma_map, 0,
|
|
|
|
sizeof(struct txp_hostvar), BUS_DMASYNC_PREREAD);
|
|
|
|
|
|
|
|
if (!wait)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
for (i = 0; i < 10000; i++) {
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_host_dma.dma_map, 0,
|
|
|
|
sizeof(struct txp_hostvar), BUS_DMASYNC_POSTREAD);
|
|
|
|
idx = le32toh(hv->hv_resp_read_idx);
|
|
|
|
if (idx != le32toh(hv->hv_resp_write_idx)) {
|
|
|
|
*rspp = NULL;
|
|
|
|
if (txp_response(sc, idx, id, seq, rspp))
|
|
|
|
return (-1);
|
|
|
|
if (*rspp != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_host_dma.dma_map, 0,
|
|
|
|
sizeof(struct txp_hostvar), BUS_DMASYNC_PREREAD);
|
|
|
|
DELAY(50);
|
|
|
|
}
|
|
|
|
if (i == 1000 || (*rspp) == NULL) {
|
|
|
|
printf("%s: 0x%x command failed\n", TXP_DEVNAME(sc), id);
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_response(struct txp_softc *sc, u_int32_t ridx, u_int16_t id, u_int16_t seq, struct txp_rsp_desc **rspp)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
struct txp_rsp_desc *rsp;
|
|
|
|
|
|
|
|
while (ridx != le32toh(hv->hv_resp_write_idx)) {
|
|
|
|
rsp = (struct txp_rsp_desc *)(((u_int8_t *)sc->sc_rspring.base) + ridx);
|
|
|
|
|
|
|
|
if (id == le16toh(rsp->rsp_id) && le16toh(rsp->rsp_seq) == seq) {
|
|
|
|
*rspp = (struct txp_rsp_desc *)malloc(
|
|
|
|
sizeof(struct txp_rsp_desc) * (rsp->rsp_numdesc + 1),
|
|
|
|
M_DEVBUF, M_NOWAIT);
|
|
|
|
if ((*rspp) == NULL)
|
|
|
|
return (-1);
|
|
|
|
txp_rsp_fixup(sc, rsp, *rspp);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rsp->rsp_flags & RSP_FLAGS_ERROR) {
|
|
|
|
printf("%s: response error: id 0x%x\n",
|
|
|
|
TXP_DEVNAME(sc), le16toh(rsp->rsp_id));
|
|
|
|
txp_rsp_fixup(sc, rsp, NULL);
|
|
|
|
ridx = le32toh(hv->hv_resp_read_idx);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (le16toh(rsp->rsp_id)) {
|
|
|
|
case TXP_CMD_CYCLE_STATISTICS:
|
|
|
|
case TXP_CMD_MEDIA_STATUS_READ:
|
|
|
|
break;
|
|
|
|
case TXP_CMD_HELLO_RESPONSE:
|
|
|
|
printf("%s: hello\n", TXP_DEVNAME(sc));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("%s: unknown id(0x%x)\n", TXP_DEVNAME(sc),
|
|
|
|
le16toh(rsp->rsp_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
txp_rsp_fixup(sc, rsp, NULL);
|
|
|
|
ridx = le32toh(hv->hv_resp_read_idx);
|
|
|
|
hv->hv_resp_read_idx = le32toh(ridx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-15 00:04:01 +03:00
|
|
|
txp_rsp_fixup(struct txp_softc *sc, struct txp_rsp_desc *rsp, struct txp_rsp_desc *dst)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_rsp_desc *src = rsp;
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
u_int32_t i, ridx;
|
|
|
|
|
|
|
|
ridx = le32toh(hv->hv_resp_read_idx);
|
|
|
|
|
|
|
|
for (i = 0; i < rsp->rsp_numdesc + 1; i++) {
|
|
|
|
if (dst != NULL)
|
2009-04-18 18:58:02 +04:00
|
|
|
memcpy(dst++, src, sizeof(struct txp_rsp_desc));
|
2003-07-02 00:08:51 +04:00
|
|
|
ridx += sizeof(struct txp_rsp_desc);
|
|
|
|
if (ridx == sc->sc_rspring.size) {
|
|
|
|
src = sc->sc_rspring.base;
|
|
|
|
ridx = 0;
|
|
|
|
} else
|
|
|
|
src++;
|
|
|
|
sc->sc_rspring.lastwrite = ridx;
|
|
|
|
hv->hv_resp_read_idx = htole32(ridx);
|
|
|
|
}
|
2005-02-27 03:26:58 +03:00
|
|
|
|
2003-07-02 00:08:51 +04:00
|
|
|
hv->hv_resp_read_idx = htole32(ridx);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_cmd_desc_numfree(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_hostvar *hv = sc->sc_hostvar;
|
|
|
|
struct txp_boot_record *br = sc->sc_boot;
|
|
|
|
u_int32_t widx, ridx, nfree;
|
|
|
|
|
|
|
|
widx = sc->sc_cmdring.lastwrite;
|
|
|
|
ridx = le32toh(hv->hv_cmd_read_idx);
|
|
|
|
|
|
|
|
if (widx == ridx) {
|
|
|
|
/* Ring is completely free */
|
|
|
|
nfree = le32toh(br->br_cmd_siz) - sizeof(struct txp_cmd_desc);
|
|
|
|
} else {
|
|
|
|
if (widx > ridx)
|
|
|
|
nfree = le32toh(br->br_cmd_siz) -
|
|
|
|
(widx - ridx + sizeof(struct txp_cmd_desc));
|
|
|
|
else
|
|
|
|
nfree = ridx - widx - sizeof(struct txp_cmd_desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (nfree / sizeof(struct txp_cmd_desc));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_stop(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
txp_command(sc, TXP_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
|
|
|
|
txp_command(sc, TXP_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1);
|
|
|
|
|
|
|
|
if (callout_pending(&sc->sc_tick))
|
|
|
|
callout_stop(&sc->sc_tick);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-11-16 04:32:37 +03:00
|
|
|
txp_watchdog(struct ifnet *ifp)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_ifmedia_upd(struct ifnet *ifp)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = ifp->if_softc;
|
|
|
|
struct ifmedia *ifm = &sc->sc_ifmedia;
|
|
|
|
u_int16_t new_xcvr;
|
|
|
|
|
|
|
|
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (IFM_SUBTYPE(ifm->ifm_media) == IFM_10_T) {
|
|
|
|
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
|
|
|
|
new_xcvr = TXP_XCVR_10_FDX;
|
|
|
|
else
|
|
|
|
new_xcvr = TXP_XCVR_10_HDX;
|
2003-07-07 19:18:24 +04:00
|
|
|
} else if ((IFM_SUBTYPE(ifm->ifm_media) == IFM_100_TX) ||
|
|
|
|
(IFM_SUBTYPE(ifm->ifm_media) == IFM_100_FX)) {
|
2003-07-02 00:08:51 +04:00
|
|
|
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
|
|
|
|
new_xcvr = TXP_XCVR_100_FDX;
|
|
|
|
else
|
|
|
|
new_xcvr = TXP_XCVR_100_HDX;
|
|
|
|
} else if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) {
|
|
|
|
new_xcvr = TXP_XCVR_AUTO;
|
|
|
|
} else
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
/* nothing to do */
|
|
|
|
if (sc->sc_xcvr == new_xcvr)
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
txp_command(sc, TXP_CMD_XCVR_SELECT, new_xcvr, 0, 0,
|
|
|
|
NULL, NULL, NULL, 0);
|
|
|
|
sc->sc_xcvr = new_xcvr;
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_softc *sc = ifp->if_softc;
|
|
|
|
struct ifmedia *ifm = &sc->sc_ifmedia;
|
|
|
|
u_int16_t bmsr, bmcr, anlpar;
|
|
|
|
|
|
|
|
ifmr->ifm_status = IFM_AVALID;
|
|
|
|
ifmr->ifm_active = IFM_ETHER;
|
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
|
|
|
|
&bmsr, NULL, NULL, 1))
|
|
|
|
goto bail;
|
|
|
|
if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMSR, 0,
|
|
|
|
&bmsr, NULL, NULL, 1))
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_BMCR, 0,
|
|
|
|
&bmcr, NULL, NULL, 1))
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_PHY_MGMT_READ, 0, MII_ANLPAR, 0,
|
|
|
|
&anlpar, NULL, NULL, 1))
|
|
|
|
goto bail;
|
|
|
|
|
|
|
|
if (bmsr & BMSR_LINK)
|
|
|
|
ifmr->ifm_status |= IFM_ACTIVE;
|
|
|
|
|
|
|
|
if (bmcr & BMCR_ISO) {
|
|
|
|
ifmr->ifm_active |= IFM_NONE;
|
|
|
|
ifmr->ifm_status = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bmcr & BMCR_LOOP)
|
|
|
|
ifmr->ifm_active |= IFM_LOOP;
|
|
|
|
|
2003-07-07 19:18:24 +04:00
|
|
|
if (!(sc->sc_flags & TXP_FIBER) && (bmcr & BMCR_AUTOEN)) {
|
2003-07-02 00:08:51 +04:00
|
|
|
if ((bmsr & BMSR_ACOMP) == 0) {
|
|
|
|
ifmr->ifm_active |= IFM_NONE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-02-16 11:00:42 +03:00
|
|
|
if (anlpar & ANLPAR_TX_FD)
|
2003-07-02 00:08:51 +04:00
|
|
|
ifmr->ifm_active |= IFM_100_TX|IFM_FDX;
|
2009-02-16 11:00:42 +03:00
|
|
|
else if (anlpar & ANLPAR_T4)
|
|
|
|
ifmr->ifm_active |= IFM_100_T4;
|
2003-07-02 00:08:51 +04:00
|
|
|
else if (anlpar & ANLPAR_TX)
|
|
|
|
ifmr->ifm_active |= IFM_100_TX;
|
|
|
|
else if (anlpar & ANLPAR_10_FD)
|
|
|
|
ifmr->ifm_active |= IFM_10_T|IFM_FDX;
|
|
|
|
else if (anlpar & ANLPAR_10)
|
|
|
|
ifmr->ifm_active |= IFM_10_T;
|
|
|
|
else
|
|
|
|
ifmr->ifm_active |= IFM_NONE;
|
|
|
|
} else
|
|
|
|
ifmr->ifm_active = ifm->ifm_cur->ifm_media;
|
|
|
|
return;
|
|
|
|
|
|
|
|
bail:
|
|
|
|
ifmr->ifm_active |= IFM_NONE;
|
|
|
|
ifmr->ifm_status &= ~IFM_AVALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_show_descriptor(void *d)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct txp_cmd_desc *cmd = d;
|
|
|
|
struct txp_rsp_desc *rsp = d;
|
|
|
|
struct txp_tx_desc *txd = d;
|
|
|
|
struct txp_frag_desc *frgd = d;
|
|
|
|
|
|
|
|
switch (cmd->cmd_flags & CMD_FLAGS_TYPE_M) {
|
|
|
|
case CMD_FLAGS_TYPE_CMD:
|
|
|
|
/* command descriptor */
|
|
|
|
printf("[cmd flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
|
|
|
|
cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
|
|
|
|
le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
|
|
|
|
le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
|
|
|
|
break;
|
|
|
|
case CMD_FLAGS_TYPE_RESP:
|
|
|
|
/* response descriptor */
|
|
|
|
printf("[rsp flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
|
|
|
|
rsp->rsp_flags, rsp->rsp_numdesc, le16toh(rsp->rsp_id),
|
|
|
|
le16toh(rsp->rsp_seq), le16toh(rsp->rsp_par1),
|
|
|
|
le32toh(rsp->rsp_par2), le32toh(rsp->rsp_par3));
|
|
|
|
break;
|
|
|
|
case CMD_FLAGS_TYPE_DATA:
|
|
|
|
/* data header (assuming tx for now) */
|
|
|
|
printf("[data flags 0x%x num %d totlen %d addr 0x%x/0x%x pflags 0x%x]",
|
|
|
|
txd->tx_flags, txd->tx_numdesc, txd->tx_totlen,
|
|
|
|
txd->tx_addrlo, txd->tx_addrhi, txd->tx_pflags);
|
|
|
|
break;
|
|
|
|
case CMD_FLAGS_TYPE_FRAG:
|
|
|
|
/* fragment descriptor */
|
|
|
|
printf("[frag flags 0x%x rsvd1 0x%x len %d addr 0x%x/0x%x rsvd2 0x%x]",
|
|
|
|
frgd->frag_flags, frgd->frag_rsvd1, frgd->frag_len,
|
|
|
|
frgd->frag_addrlo, frgd->frag_addrhi, frgd->frag_rsvd2);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printf("[unknown(%x) flags 0x%x num %d id %d seq %d par1 0x%x par2 0x%x par3 0x%x]\n",
|
|
|
|
cmd->cmd_flags & CMD_FLAGS_TYPE_M,
|
|
|
|
cmd->cmd_flags, cmd->cmd_numdesc, le16toh(cmd->cmd_id),
|
|
|
|
le16toh(cmd->cmd_seq), le16toh(cmd->cmd_par1),
|
|
|
|
le32toh(cmd->cmd_par2), le32toh(cmd->cmd_par3));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_set_filter(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ethercom *ac = &sc->sc_arpcom;
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
u_int32_t crc, carry, hashbit, hash[2];
|
|
|
|
u_int16_t filter;
|
|
|
|
u_int8_t octet;
|
|
|
|
int i, j, mcnt = 0;
|
|
|
|
struct ether_multi *enm;
|
|
|
|
struct ether_multistep step;
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
|
|
filter = TXP_RXFILT_PROMISC;
|
|
|
|
goto setit;
|
|
|
|
}
|
|
|
|
|
|
|
|
again:
|
|
|
|
filter = TXP_RXFILT_DIRECT;
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_BROADCAST)
|
|
|
|
filter |= TXP_RXFILT_BROADCAST;
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_ALLMULTI)
|
|
|
|
filter |= TXP_RXFILT_ALLMULTI;
|
|
|
|
else {
|
|
|
|
hash[0] = hash[1] = 0;
|
|
|
|
|
|
|
|
ETHER_FIRST_MULTI(step, ac, enm);
|
|
|
|
while (enm != NULL) {
|
2009-03-18 18:14:29 +03:00
|
|
|
if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
|
2003-07-02 00:08:51 +04:00
|
|
|
/*
|
|
|
|
* We must listen to a range of multicast
|
|
|
|
* addresses. For now, just accept all
|
|
|
|
* multicasts, rather than trying to set only
|
|
|
|
* those filter bits needed to match the range.
|
|
|
|
* (At this time, the only use of address
|
|
|
|
* ranges is for IP multicast routing, for
|
|
|
|
* which the range is big enough to require
|
|
|
|
* all bits set.)
|
|
|
|
*/
|
|
|
|
ifp->if_flags |= IFF_ALLMULTI;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
mcnt++;
|
|
|
|
crc = 0xffffffff;
|
|
|
|
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
|
|
octet = enm->enm_addrlo[i];
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
carry = ((crc & 0x80000000) ? 1 : 0) ^
|
|
|
|
(octet & 1);
|
|
|
|
crc <<= 1;
|
|
|
|
octet >>= 1;
|
|
|
|
if (carry)
|
|
|
|
crc = (crc ^ TXP_POLYNOMIAL) |
|
|
|
|
carry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hashbit = (u_int16_t)(crc & (64 - 1));
|
|
|
|
hash[hashbit / 32] |= (1 << hashbit % 32);
|
|
|
|
ETHER_NEXT_MULTI(step, enm);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mcnt > 0) {
|
|
|
|
filter |= TXP_RXFILT_HASHMULTI;
|
|
|
|
txp_command(sc, TXP_CMD_MCAST_HASH_MASK_WRITE,
|
|
|
|
2, hash[0], hash[1], NULL, NULL, NULL, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setit:
|
|
|
|
txp_command(sc, TXP_CMD_RX_FILTER_WRITE, filter, 0, 0,
|
|
|
|
NULL, NULL, NULL, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-03-14 18:35:58 +03:00
|
|
|
txp_capabilities(struct txp_softc *sc)
|
2003-07-02 00:08:51 +04:00
|
|
|
{
|
|
|
|
struct ifnet *ifp = &sc->sc_arpcom.ec_if;
|
|
|
|
struct txp_rsp_desc *rsp = NULL;
|
|
|
|
struct txp_ext_desc *ext;
|
|
|
|
|
|
|
|
if (txp_command2(sc, TXP_CMD_OFFLOAD_READ, 0, 0, 0, NULL, 0, &rsp, 1))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
if (rsp->rsp_numdesc != 1)
|
|
|
|
goto out;
|
|
|
|
ext = (struct txp_ext_desc *)(rsp + 1);
|
|
|
|
|
|
|
|
sc->sc_tx_capability = ext->ext_1 & OFFLOAD_MASK;
|
|
|
|
sc->sc_rx_capability = ext->ext_2 & OFFLOAD_MASK;
|
|
|
|
|
2003-08-20 21:41:38 +04:00
|
|
|
sc->sc_arpcom.ec_capabilities |= ETHERCAP_VLAN_MTU;
|
2003-07-02 00:08:51 +04:00
|
|
|
if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_VLAN) {
|
|
|
|
sc->sc_tx_capability |= OFFLOAD_VLAN;
|
|
|
|
sc->sc_rx_capability |= OFFLOAD_VLAN;
|
2003-08-20 21:41:38 +04:00
|
|
|
sc->sc_arpcom.ec_capabilities |= ETHERCAP_VLAN_HWTAGGING;
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/* not ready yet */
|
|
|
|
if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPSEC) {
|
|
|
|
sc->sc_tx_capability |= OFFLOAD_IPSEC;
|
|
|
|
sc->sc_rx_capability |= OFFLOAD_IPSEC;
|
|
|
|
ifp->if_capabilities |= IFCAP_IPSEC;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_IPCKSUM) {
|
|
|
|
sc->sc_tx_capability |= OFFLOAD_IPCKSUM;
|
|
|
|
sc->sc_rx_capability |= OFFLOAD_IPCKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
ifp->if_capabilities |= IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx;
|
2003-07-02 00:08:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_TCPCKSUM) {
|
|
|
|
sc->sc_rx_capability |= OFFLOAD_TCPCKSUM;
|
|
|
|
#ifdef TRY_TX_TCP_CSUM
|
|
|
|
sc->sc_tx_capability |= OFFLOAD_TCPCKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
ifp->if_capabilities |=
|
|
|
|
IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx;
|
2003-07-02 00:08:51 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rsp->rsp_par2 & rsp->rsp_par3 & OFFLOAD_UDPCKSUM) {
|
|
|
|
sc->sc_rx_capability |= OFFLOAD_UDPCKSUM;
|
|
|
|
#ifdef TRY_TX_UDP_CSUM
|
|
|
|
sc->sc_tx_capability |= OFFLOAD_UDPCKSUM;
|
2005-05-02 19:34:31 +04:00
|
|
|
ifp->if_capabilities |=
|
|
|
|
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx;
|
2003-07-02 00:08:51 +04:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (txp_command(sc, TXP_CMD_OFFLOAD_WRITE, 0,
|
|
|
|
sc->sc_tx_capability, sc->sc_rx_capability, NULL, NULL, NULL, 1))
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (rsp != NULL)
|
|
|
|
free(rsp, M_DEVBUF);
|
|
|
|
}
|