Re: em(4) VLAN + PROMISC still doesn't work with latest CVS version
From: Iasen Kostov (tbyte_at_OTEL.net)
Date: 12/08/04
- Previous message: Konstantin KABASSANOV: "the correct ipv6 behavior for interfaces with gif tunnel on them"
- In reply to: Robert Watson: "Re: em(4) VLAN + PROMISC still doesn't work with latest CVS version"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Wed, 08 Dec 2004 15:05:12 +0200 To: Robert Watson <rwatson@freebsd.org>
Robert Watson wrote:
>On Wed, 8 Dec 2004, Iasen Kostov wrote:
>
>
>
>>#:> ident if_em.c
>>if_em.c:
>> $FreeBSD: src/sys/dev/em/if_em.c,v 1.44.2.4 2004/11/23 22:28:40
>>rwatson Exp $
>>
>>I've deleted the whole dir and cvsuped. Does 1.54 differs much from
>>1.44.2.4 ?
>>
>>
>
>Very odd. I have 1.44.2.4 checked out here, and if_em.diff applied
>without a hitch. Here's a version of the patch generated using cvs diff
>against the older revision. It should be basically identical to the
>version I sent you before, though. Diffing the two diffs generates only
>line number differences.
>
>If this doesn't work, drop me a copy of your if_em.c and I'll apply the
>change manually.
>
>
>
Same happens - here is the if_em.c
/**************************************************************************
Copyright (c) 2001-2003, Intel Corporation
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.
3. Neither the name of the Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
***************************************************************************/
/*$FreeBSD: src/sys/dev/em/if_em.c,v 1.44.2.4 2004/11/23 22:28:40 rwatson Exp $*/
#include <dev/em/if_em.h>
/*********************************************************************
* Set this to one to display debug statistics
*********************************************************************/
int em_display_debug_stats = 0;
/*********************************************************************
* Linked list of board private structures for all NICs found
*********************************************************************/
struct adapter *em_adapter_list = NULL;
/*********************************************************************
* Driver version
*********************************************************************/
char em_driver_version[] = "1.7.35";
/*********************************************************************
* PCI Device ID Table
*
* Used by probe to select devices to load on
* Last field stores an index into em_strings
* Last entry must be all 0s
*
* { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index }
*********************************************************************/
static em_vendor_info_t em_vendor_info_array[] =
{
/* Intel(R) PRO/1000 Network Connection */
{ 0x8086, 0x1000, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1001, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1004, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1008, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1009, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x100C, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x100D, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x100E, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x100F, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1010, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1011, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1013, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1015, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1016, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1017, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1018, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1019, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x101A, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x101D, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x101E, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1026, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1027, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1028, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1075, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1076, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1077, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1078, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x1079, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x107A, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x107B, PCI_ANY_ID, PCI_ANY_ID, 0},
{ 0x8086, 0x107C, PCI_ANY_ID, PCI_ANY_ID, 0},
/* required last entry */
{ 0, 0, 0, 0, 0}
};
/*********************************************************************
* Table of branding strings for all supported NICs.
*********************************************************************/
static char *em_strings[] = {
"Intel(R) PRO/1000 Network Connection"
};
/*********************************************************************
* Function prototypes
*********************************************************************/
static int em_probe(device_t);
static int em_attach(device_t);
static int em_detach(device_t);
static int em_shutdown(device_t);
static void em_intr(void *);
static void em_start(struct ifnet *);
static int em_ioctl(struct ifnet *, u_long, caddr_t);
static void em_watchdog(struct ifnet *);
static void em_init(void *);
static void em_init_locked(struct adapter *);
static void em_stop(void *);
static void em_media_status(struct ifnet *, struct ifmediareq *);
static int em_media_change(struct ifnet *);
static void em_identify_hardware(struct adapter *);
static int em_allocate_pci_resources(struct adapter *);
static void em_free_pci_resources(struct adapter *);
static void em_local_timer(void *);
static int em_hardware_init(struct adapter *);
static void em_setup_interface(device_t, struct adapter *);
static int em_setup_transmit_structures(struct adapter *);
static void em_initialize_transmit_unit(struct adapter *);
static int em_setup_receive_structures(struct adapter *);
static void em_initialize_receive_unit(struct adapter *);
static void em_enable_intr(struct adapter *);
static void em_disable_intr(struct adapter *);
static void em_free_transmit_structures(struct adapter *);
static void em_free_receive_structures(struct adapter *);
static void em_update_stats_counters(struct adapter *);
static void em_clean_transmit_interrupts(struct adapter *);
static int em_allocate_receive_structures(struct adapter *);
static int em_allocate_transmit_structures(struct adapter *);
static void em_process_receive_interrupts(struct adapter *, int);
static void em_receive_checksum(struct adapter *,
struct em_rx_desc *,
struct mbuf *);
static void em_transmit_checksum_setup(struct adapter *,
struct mbuf *,
u_int32_t *,
u_int32_t *);
static void em_set_promisc(struct adapter *);
static void em_disable_promisc(struct adapter *);
static void em_set_multi(struct adapter *);
static void em_print_hw_stats(struct adapter *);
static void em_print_link_status(struct adapter *);
static int em_get_buf(int i, struct adapter *,
struct mbuf *);
static void em_enable_vlans(struct adapter *);
static int em_encap(struct adapter *, struct mbuf **);
static void em_smartspeed(struct adapter *);
static int em_82547_fifo_workaround(struct adapter *, int);
static void em_82547_update_fifo_head(struct adapter *, int);
static int em_82547_tx_fifo_reset(struct adapter *);
static void em_82547_move_tail(void *arg);
static void em_82547_move_tail_locked(struct adapter *);
static int em_dma_malloc(struct adapter *, bus_size_t,
struct em_dma_alloc *, int);
static void em_dma_free(struct adapter *, struct em_dma_alloc *);
static void em_print_debug_info(struct adapter *);
static int em_is_valid_ether_addr(u_int8_t *);
static int em_sysctl_stats(SYSCTL_HANDLER_ARGS);
static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS);
static u_int32_t em_fill_descriptors (u_int64_t address,
u_int32_t length,
PDESC_ARRAY desc_array);
static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS);
static void em_add_int_delay_sysctl(struct adapter *, const char *,
const char *, struct em_int_delay_info *,
int, int);
/*********************************************************************
* FreeBSD Device Interface Entry Points
*********************************************************************/
static device_method_t em_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, em_probe),
DEVMETHOD(device_attach, em_attach),
DEVMETHOD(device_detach, em_detach),
DEVMETHOD(device_shutdown, em_shutdown),
{0, 0}
};
static driver_t em_driver = {
"em", em_methods, sizeof(struct adapter ),
};
static devclass_t em_devclass;
DRIVER_MODULE(em, pci, em_driver, em_devclass, 0, 0);
MODULE_DEPEND(em, pci, 1, 1, 1);
MODULE_DEPEND(em, ether, 1, 1, 1);
/*********************************************************************
* Tunable default values.
*********************************************************************/
#define E1000_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000)
#define E1000_USECS_TO_TICKS(usecs) ((1000 * (usecs) + 512) / 1024)
static int em_tx_int_delay_dflt = E1000_TICKS_TO_USECS(EM_TIDV);
static int em_rx_int_delay_dflt = E1000_TICKS_TO_USECS(EM_RDTR);
static int em_tx_abs_int_delay_dflt = E1000_TICKS_TO_USECS(EM_TADV);
static int em_rx_abs_int_delay_dflt = E1000_TICKS_TO_USECS(EM_RADV);
TUNABLE_INT("hw.em.tx_int_delay", &em_tx_int_delay_dflt);
TUNABLE_INT("hw.em.rx_int_delay", &em_rx_int_delay_dflt);
TUNABLE_INT("hw.em.tx_abs_int_delay", &em_tx_abs_int_delay_dflt);
TUNABLE_INT("hw.em.rx_abs_int_delay", &em_rx_abs_int_delay_dflt);
/*********************************************************************
* Device identification routine
*
* em_probe determines if the driver should be loaded on
* adapter based on PCI vendor/device id of the adapter.
*
* return 0 on success, positive on failure
*********************************************************************/
static int
em_probe(device_t dev)
{
em_vendor_info_t *ent;
u_int16_t pci_vendor_id = 0;
u_int16_t pci_device_id = 0;
u_int16_t pci_subvendor_id = 0;
u_int16_t pci_subdevice_id = 0;
char adapter_name[60];
INIT_DEBUGOUT("em_probe: begin");
pci_vendor_id = pci_get_vendor(dev);
if (pci_vendor_id != EM_VENDOR_ID)
return(ENXIO);
pci_device_id = pci_get_device(dev);
pci_subvendor_id = pci_get_subvendor(dev);
pci_subdevice_id = pci_get_subdevice(dev);
ent = em_vendor_info_array;
while (ent->vendor_id != 0) {
if ((pci_vendor_id == ent->vendor_id) &&
(pci_device_id == ent->device_id) &&
((pci_subvendor_id == ent->subvendor_id) ||
(ent->subvendor_id == PCI_ANY_ID)) &&
((pci_subdevice_id == ent->subdevice_id) ||
(ent->subdevice_id == PCI_ANY_ID))) {
sprintf(adapter_name, "%s, Version - %s",
em_strings[ent->index],
em_driver_version);
device_set_desc_copy(dev, adapter_name);
return(0);
}
ent++;
}
return(ENXIO);
}
/*********************************************************************
* Device initialization routine
*
* The attach entry point is called when the driver is being loaded.
* This routine identifies the type of hardware, allocates all resources
* and initializes the hardware.
*
* return 0 on success, positive on failure
*********************************************************************/
static int
em_attach(device_t dev)
{
struct adapter * adapter;
int tsize, rsize;
int error = 0;
INIT_DEBUGOUT("em_attach: begin");
/* Allocate, clear, and link in our adapter structure */
if (!(adapter = device_get_softc(dev))) {
printf("em: adapter structure allocation failed\n");
return(ENOMEM);
}
bzero(adapter, sizeof(struct adapter ));
adapter->dev = dev;
adapter->osdep.dev = dev;
adapter->unit = device_get_unit(dev);
EM_LOCK_INIT(adapter, device_get_nameunit(dev));
if (em_adapter_list != NULL)
em_adapter_list->prev = adapter;
adapter->next = em_adapter_list;
em_adapter_list = adapter;
/* SYSCTL stuff */
sysctl_ctx_init(&adapter->sysctl_ctx);
adapter->sysctl_tree = SYSCTL_ADD_NODE(&adapter->sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_hw),
OID_AUTO,
device_get_nameunit(dev),
CTLFLAG_RD,
0, "");
if (adapter->sysctl_tree == NULL) {
error = EIO;
goto err_sysctl;
}
SYSCTL_ADD_PROC(&adapter->sysctl_ctx,
SYSCTL_CHILDREN(adapter->sysctl_tree),
OID_AUTO, "debug_info", CTLTYPE_INT|CTLFLAG_RW,
(void *)adapter, 0,
em_sysctl_debug_info, "I", "Debug Information");
SYSCTL_ADD_PROC(&adapter->sysctl_ctx,
SYSCTL_CHILDREN(adapter->sysctl_tree),
OID_AUTO, "stats", CTLTYPE_INT|CTLFLAG_RW,
(void *)adapter, 0,
em_sysctl_stats, "I", "Statistics");
callout_init(&adapter->timer, CALLOUT_MPSAFE);
callout_init(&adapter->tx_fifo_timer, CALLOUT_MPSAFE);
/* Determine hardware revision */
em_identify_hardware(adapter);
/* Set up some sysctls for the tunable interrupt delays */
em_add_int_delay_sysctl(adapter, "rx_int_delay",
"receive interrupt delay in usecs", &adapter->rx_int_delay,
E1000_REG_OFFSET(&adapter->hw, RDTR), em_rx_int_delay_dflt);
em_add_int_delay_sysctl(adapter, "tx_int_delay",
"transmit interrupt delay in usecs", &adapter->tx_int_delay,
E1000_REG_OFFSET(&adapter->hw, TIDV), em_tx_int_delay_dflt);
if (adapter->hw.mac_type >= em_82540) {
em_add_int_delay_sysctl(adapter, "rx_abs_int_delay",
"receive interrupt delay limit in usecs",
&adapter->rx_abs_int_delay,
E1000_REG_OFFSET(&adapter->hw, RADV),
em_rx_abs_int_delay_dflt);
em_add_int_delay_sysctl(adapter, "tx_abs_int_delay",
"transmit interrupt delay limit in usecs",
&adapter->tx_abs_int_delay,
E1000_REG_OFFSET(&adapter->hw, TADV),
em_tx_abs_int_delay_dflt);
}
/* Parameters (to be read from user) */
adapter->num_tx_desc = EM_MAX_TXD;
adapter->num_rx_desc = EM_MAX_RXD;
adapter->hw.autoneg = DO_AUTO_NEG;
adapter->hw.wait_autoneg_complete = WAIT_FOR_AUTO_NEG_DEFAULT;
adapter->hw.autoneg_advertised = AUTONEG_ADV_DEFAULT;
adapter->hw.tbi_compatibility_en = TRUE;
adapter->rx_buffer_len = EM_RXBUFFER_2048;
/*
* These parameters control the automatic generation(Tx) and
* response(Rx) to Ethernet PAUSE frames.
*/
adapter->hw.fc_high_water = FC_DEFAULT_HI_THRESH;
adapter->hw.fc_low_water = FC_DEFAULT_LO_THRESH;
adapter->hw.fc_pause_time = FC_DEFAULT_TX_TIMER;
adapter->hw.fc_send_xon = TRUE;
adapter->hw.fc = em_fc_full;
adapter->hw.phy_init_script = 1;
adapter->hw.phy_reset_disable = FALSE;
#ifndef EM_MASTER_SLAVE
adapter->hw.master_slave = em_ms_hw_default;
#else
adapter->hw.master_slave = EM_MASTER_SLAVE;
#endif
/*
* Set the max frame size assuming standard ethernet
* sized frames
*/
adapter->hw.max_frame_size =
ETHERMTU + ETHER_HDR_LEN + ETHER_CRC_LEN;
adapter->hw.min_frame_size =
MINIMUM_ETHERNET_PACKET_SIZE + ETHER_CRC_LEN;
/*
* This controls when hardware reports transmit completion
* status.
*/
adapter->hw.report_tx_early = 1;
if (em_allocate_pci_resources(adapter)) {
printf("em%d: Allocation of PCI resources failed\n",
adapter->unit);
error = ENXIO;
goto err_pci;
}
/* Initialize eeprom parameters */
em_init_eeprom_params(&adapter->hw);
tsize = EM_ROUNDUP(adapter->num_tx_desc *
sizeof(struct em_tx_desc), 4096);
/* Allocate Transmit Descriptor ring */
if (em_dma_malloc(adapter, tsize, &adapter->txdma, BUS_DMA_NOWAIT)) {
printf("em%d: Unable to allocate tx_desc memory\n",
adapter->unit);
error = ENOMEM;
goto err_tx_desc;
}
adapter->tx_desc_base = (struct em_tx_desc *) adapter->txdma.dma_vaddr;
rsize = EM_ROUNDUP(adapter->num_rx_desc *
sizeof(struct em_rx_desc), 4096);
/* Allocate Receive Descriptor ring */
if (em_dma_malloc(adapter, rsize, &adapter->rxdma, BUS_DMA_NOWAIT)) {
printf("em%d: Unable to allocate rx_desc memory\n",
adapter->unit);
error = ENOMEM;
goto err_rx_desc;
}
adapter->rx_desc_base = (struct em_rx_desc *) adapter->rxdma.dma_vaddr;
/* Initialize the hardware */
if (em_hardware_init(adapter)) {
printf("em%d: Unable to initialize the hardware\n",
adapter->unit);
error = EIO;
goto err_hw_init;
}
/* Copy the permanent MAC address out of the EEPROM */
if (em_read_mac_addr(&adapter->hw) < 0) {
printf("em%d: EEPROM read error while reading mac address\n",
adapter->unit);
error = EIO;
goto err_mac_addr;
}
if (!em_is_valid_ether_addr(adapter->hw.mac_addr)) {
printf("em%d: Invalid mac address\n", adapter->unit);
error = EIO;
goto err_mac_addr;
}
bcopy(adapter->hw.mac_addr, adapter->interface_data.ac_enaddr,
ETHER_ADDR_LEN);
/* Setup OS specific network interface */
em_setup_interface(dev, adapter);
/* Initialize statistics */
em_clear_hw_cntrs(&adapter->hw);
em_update_stats_counters(adapter);
adapter->hw.get_link_status = 1;
em_check_for_link(&adapter->hw);
/* Print the link status */
if (adapter->link_active == 1) {
em_get_speed_and_duplex(&adapter->hw, &adapter->link_speed,
&adapter->link_duplex);
printf("em%d: Speed:%d Mbps Duplex:%s\n",
adapter->unit,
adapter->link_speed,
adapter->link_duplex == FULL_DUPLEX ? "Full" : "Half");
} else
printf("em%d: Speed:N/A Duplex:N/A\n", adapter->unit);
/* Identify 82544 on PCIX */
em_get_bus_info(&adapter->hw);
if(adapter->hw.bus_type == em_bus_type_pcix &&
adapter->hw.mac_type == em_82544) {
adapter->pcix_82544 = TRUE;
}
else {
adapter->pcix_82544 = FALSE;
}
INIT_DEBUGOUT("em_attach: end");
return(0);
err_mac_addr:
err_hw_init:
em_dma_free(adapter, &adapter->rxdma);
err_rx_desc:
em_dma_free(adapter, &adapter->txdma);
err_tx_desc:
err_pci:
em_free_pci_resources(adapter);
sysctl_ctx_free(&adapter->sysctl_ctx);
err_sysctl:
return(error);
}
/*********************************************************************
* Device removal routine
*
* The detach entry point is called when the driver is being removed.
* This routine stops the adapter and deallocates all the resources
* that were allocated for driver operation.
*
* return 0 on success, positive on failure
*********************************************************************/
static int
em_detach(device_t dev)
{
struct adapter * adapter = device_get_softc(dev);
struct ifnet *ifp = &adapter->interface_data.ac_if;
INIT_DEBUGOUT("em_detach: begin");
EM_LOCK(adapter);
adapter->in_detach = 1;
em_stop(adapter);
em_phy_hw_reset(&adapter->hw);
EM_UNLOCK(adapter);
#if __FreeBSD_version < 500000
ether_ifdetach(&adapter->interface_data.ac_if, ETHER_BPF_SUPPORTED);
#else
ether_ifdetach(&adapter->interface_data.ac_if);
#endif
em_free_pci_resources(adapter);
bus_generic_detach(dev);
/* Free Transmit Descriptor ring */
if (adapter->tx_desc_base) {
em_dma_free(adapter, &adapter->txdma);
adapter->tx_desc_base = NULL;
}
/* Free Receive Descriptor ring */
if (adapter->rx_desc_base) {
em_dma_free(adapter, &adapter->rxdma);
adapter->rx_desc_base = NULL;
}
/* Free the sysctl tree */
sysctl_ctx_free(&adapter->sysctl_ctx);
/* Remove from the adapter list */
if (em_adapter_list == adapter)
em_adapter_list = adapter->next;
if (adapter->next != NULL)
adapter->next->prev = adapter->prev;
if (adapter->prev != NULL)
adapter->prev->next = adapter->next;
EM_LOCK_DESTROY(adapter);
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
ifp->if_timer = 0;
return(0);
}
/*********************************************************************
*
* Shutdown entry point
*
**********************************************************************/
static int
em_shutdown(device_t dev)
{
struct adapter *adapter = device_get_softc(dev);
EM_LOCK(adapter);
em_stop(adapter);
EM_UNLOCK(adapter);
return(0);
}
/*********************************************************************
* Transmit entry point
*
* em_start is called by the stack to initiate a transmit.
* The driver will remain in this routine as long as there are
* packets to transmit and transmit resources are available.
* In case resources are not available stack is notified and
* the packet is requeued.
**********************************************************************/
static void
em_start_locked(struct ifnet *ifp)
{
struct mbuf *m_head;
struct adapter *adapter = ifp->if_softc;
mtx_assert(&adapter->mtx, MA_OWNED);
if (!adapter->link_active)
return;
while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL) break;
/*
* em_encap() can modify our pointer, and or make it NULL on
* failure. In that event, we can't requeue.
*/
if (em_encap(adapter, &m_head)) {
if (m_head == NULL)
break;
ifp->if_flags |= IFF_OACTIVE;
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
break;
}
/* Send a copy of the frame to the BPF listener */
#if __FreeBSD_version < 500000
if (ifp->if_bpf)
bpf_mtap(ifp, m_head);
#else
BPF_MTAP(ifp, m_head);
#endif
/* Set timeout in case hardware has problems transmitting */
ifp->if_timer = EM_TX_TIMEOUT;
}
return;
}
static void
em_start(struct ifnet *ifp)
{
struct adapter *adapter = ifp->if_softc;
EM_LOCK(adapter);
em_start_locked(ifp);
EM_UNLOCK(adapter);
return;
}
/*********************************************************************
* Ioctl entry point
*
* em_ioctl is called when the user wants to configure the
* interface.
*
* return 0 on success, positive on failure
**********************************************************************/
static int
em_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
int mask, reinit, error = 0;
struct ifreq *ifr = (struct ifreq *) data;
struct adapter * adapter = ifp->if_softc;
if (adapter->in_detach) return(error);
switch (command) {
case SIOCSIFADDR:
case SIOCGIFADDR:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCxIFADDR (Get/Set Interface Addr)");
ether_ioctl(ifp, command, data);
break;
case SIOCSIFMTU:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)");
if (ifr->ifr_mtu > MAX_JUMBO_FRAME_SIZE - ETHER_HDR_LEN) {
error = EINVAL;
} else {
EM_LOCK(adapter);
ifp->if_mtu = ifr->ifr_mtu;
adapter->hw.max_frame_size =
ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
em_init_locked(adapter);
EM_UNLOCK(adapter);
}
break;
case SIOCSIFFLAGS:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFFLAGS (Set Interface Flags)");
EM_LOCK(adapter);
if (ifp->if_flags & IFF_UP) {
if (!(ifp->if_flags & IFF_RUNNING)) {
em_init_locked(adapter);
}
em_disable_promisc(adapter);
em_set_promisc(adapter);
} else {
if (ifp->if_flags & IFF_RUNNING) {
em_stop(adapter);
}
}
EM_UNLOCK(adapter);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOC(ADD|DEL)MULTI");
if (ifp->if_flags & IFF_RUNNING) {
EM_LOCK(adapter);
em_disable_intr(adapter);
em_set_multi(adapter);
if (adapter->hw.mac_type == em_82542_rev2_0) {
em_initialize_receive_unit(adapter);
}
#ifdef DEVICE_POLLING
if (!(ifp->if_flags & IFF_POLLING))
#endif
em_enable_intr(adapter);
EM_UNLOCK(adapter);
}
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCxIFMEDIA (Get/Set Interface Media)");
error = ifmedia_ioctl(ifp, ifr, &adapter->media, command);
break;
case SIOCSIFCAP:
IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFCAP (Set Capabilities)");
reinit = 0;
mask = ifr->ifr_reqcap ^ ifp->if_capenable;
if (mask & IFCAP_POLLING)
ifp->if_capenable ^= IFCAP_POLLING;
if (mask & IFCAP_HWCSUM) {
ifp->if_capenable ^= IFCAP_HWCSUM;
reinit = 1;
}
if (mask & IFCAP_VLAN_HWTAGGING) {
ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
reinit = 1;
}
if (reinit && (ifp->if_flags & IFF_RUNNING))
em_init(adapter);
break;
default:
IOCTL_DEBUGOUT1("ioctl received: UNKNOWN (0x%x)", (int)command);
error = EINVAL;
}
return(error);
}
/*********************************************************************
* Watchdog entry point
*
* This routine is called whenever hardware quits transmitting.
*
**********************************************************************/
static void
em_watchdog(struct ifnet *ifp)
{
struct adapter * adapter;
adapter = ifp->if_softc;
/* If we are in this routine because of pause frames, then
* don't reset the hardware.
*/
if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_TXOFF) {
ifp->if_timer = EM_TX_TIMEOUT;
return;
}
if (em_check_for_link(&adapter->hw))
printf("em%d: watchdog timeout -- resetting\n", adapter->unit);
ifp->if_flags &= ~IFF_RUNNING;
em_init(adapter);
ifp->if_oerrors++;
return;
}
/*********************************************************************
* Init entry point
*
* This routine is used in two ways. It is used by the stack as
* init entry point in network interface structure. It is also used
* by the driver as a hw/sw initialization routine to get to a
* consistent state.
*
* return 0 on success, positive on failure
**********************************************************************/
static void
em_init_locked(struct adapter * adapter)
{
struct ifnet *ifp;
uint32_t pba;
ifp = &adapter->interface_data.ac_if;
INIT_DEBUGOUT("em_init: begin");
mtx_assert(&adapter->mtx, MA_OWNED);
em_stop(adapter);
/* Packet Buffer Allocation (PBA)
* Writing PBA sets the receive portion of the buffer
* the remainder is used for the transmit buffer.
*
* Devices before the 82547 had a Packet Buffer of 64K.
* Default allocation: PBA=48K for Rx, leaving 16K for Tx.
* After the 82547 the buffer was reduced to 40K.
* Default allocation: PBA=30K for Rx, leaving 10K for Tx.
* Note: default does not leave enough room for Jumbo Frame >10k.
*/
if(adapter->hw.mac_type < em_82547) {
/* Total FIFO is 64K */
if(adapter->rx_buffer_len > EM_RXBUFFER_8192)
pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */
else
pba = E1000_PBA_48K; /* 48K for Rx, 16K for Tx */
} else {
/* Total FIFO is 40K */
if(adapter->hw.max_frame_size > EM_RXBUFFER_8192) {
pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */
} else {
pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */
}
adapter->tx_fifo_head = 0;
adapter->tx_head_addr = pba << EM_TX_HEAD_ADDR_SHIFT;
adapter->tx_fifo_size = (E1000_PBA_40K - pba) << EM_PBA_BYTES_SHIFT;
}
INIT_DEBUGOUT1("em_init: pba=%dK",pba);
E1000_WRITE_REG(&adapter->hw, PBA, pba);
/* Get the latest mac address, User can use a LAA */
bcopy(adapter->interface_data.ac_enaddr, adapter->hw.mac_addr,
ETHER_ADDR_LEN);
/* Initialize the hardware */
if (em_hardware_init(adapter)) {
printf("em%d: Unable to initialize the hardware\n",
adapter->unit);
return;
}
if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING)
em_enable_vlans(adapter);
/* Prepare transmit descriptors and buffers */
if (em_setup_transmit_structures(adapter)) {
printf("em%d: Could not setup transmit structures\n",
adapter->unit);
em_stop(adapter);
return;
}
em_initialize_transmit_unit(adapter);
/* Setup Multicast table */
em_set_multi(adapter);
/* Prepare receive descriptors and buffers */
if (em_setup_receive_structures(adapter)) {
printf("em%d: Could not setup receive structures\n",
adapter->unit);
em_stop(adapter);
return;
}
em_initialize_receive_unit(adapter);
/* Don't loose promiscuous settings */
em_set_promisc(adapter);
ifp->if_flags |= IFF_RUNNING;
ifp->if_flags &= ~IFF_OACTIVE;
if (adapter->hw.mac_type >= em_82543) {
if (ifp->if_capenable & IFCAP_TXCSUM)
ifp->if_hwassist = EM_CHECKSUM_FEATURES;
else
ifp->if_hwassist = 0;
}
callout_reset(&adapter->timer, 2*hz, em_local_timer, adapter);
em_clear_hw_cntrs(&adapter->hw);
#ifdef DEVICE_POLLING
/*
* Only enable interrupts if we are not polling, make sure
* they are off otherwise.
*/
if (ifp->if_flags & IFF_POLLING)
em_disable_intr(adapter);
else
#endif /* DEVICE_POLLING */
em_enable_intr(adapter);
/* Don't reset the phy next time init gets called */
adapter->hw.phy_reset_disable = TRUE;
return;
}
static void
em_init(void *arg)
{
struct adapter * adapter = arg;
EM_LOCK(adapter);
em_init_locked(adapter);
EM_UNLOCK(adapter);
return;
}
#ifdef DEVICE_POLLING
static poll_handler_t em_poll;
static void
em_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
struct adapter *adapter = ifp->if_softc;
u_int32_t reg_icr;
mtx_assert(&adapter->mtx, MA_OWNED);
if (!(ifp->if_capenable & IFCAP_POLLING)) {
ether_poll_deregister(ifp);
cmd = POLL_DEREGISTER;
}
if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
em_enable_intr(adapter);
return;
}
if (cmd == POLL_AND_CHECK_STATUS) {
reg_icr = E1000_READ_REG(&adapter->hw, ICR);
if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
callout_stop(&adapter->timer);
adapter->hw.get_link_status = 1;
em_check_for_link(&adapter->hw);
em_print_link_status(adapter);
callout_reset(&adapter->timer, 2*hz, em_local_timer, adapter);
}
}
if (ifp->if_flags & IFF_RUNNING) {
em_process_receive_interrupts(adapter, count);
em_clean_transmit_interrupts(adapter);
}
if (ifp->if_flags & IFF_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
em_start_locked(ifp);
}
static void
em_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
struct adapter *adapter = ifp->if_softc;
EM_LOCK(adapter);
em_poll_locked(ifp, cmd, count);
EM_UNLOCK(adapter);
}
#endif /* DEVICE_POLLING */
/*********************************************************************
*
* Interrupt Service routine
*
**********************************************************************/
static void
em_intr(void *arg)
{
u_int32_t loop_cnt = EM_MAX_INTR;
u_int32_t reg_icr;
struct ifnet *ifp;
struct adapter *adapter = arg;
EM_LOCK(adapter);
ifp = &adapter->interface_data.ac_if;
#ifdef DEVICE_POLLING
if (ifp->if_flags & IFF_POLLING) {
EM_UNLOCK(adapter);
return;
}
if ((ifp->if_capenable & IFCAP_POLLING) &&
ether_poll_register(em_poll, ifp)) {
em_disable_intr(adapter);
em_poll_locked(ifp, 0, 1);
EM_UNLOCK(adapter);
return;
}
#endif /* DEVICE_POLLING */
reg_icr = E1000_READ_REG(&adapter->hw, ICR);
if (!reg_icr) {
EM_UNLOCK(adapter);
return;
}
/* Link status change */
if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) {
callout_stop(&adapter->timer);
adapter->hw.get_link_status = 1;
em_check_for_link(&adapter->hw);
em_print_link_status(adapter);
callout_reset(&adapter->timer, 2*hz, em_local_timer, adapter);
}
while (loop_cnt > 0) {
if (ifp->if_flags & IFF_RUNNING) {
em_process_receive_interrupts(adapter, -1);
em_clean_transmit_interrupts(adapter);
}
loop_cnt--;
}
if (ifp->if_flags & IFF_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd))
em_start_locked(ifp);
EM_UNLOCK(adapter);
return;
}
/*********************************************************************
*
* Media Ioctl callback
*
* This routine is called whenever the user queries the status of
* the interface using ifconfig.
*
**********************************************************************/
static void
em_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct adapter * adapter = ifp->if_softc;
INIT_DEBUGOUT("em_media_status: begin");
em_check_for_link(&adapter->hw);
if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) {
if (adapter->link_active == 0) {
em_get_speed_and_duplex(&adapter->hw,
&adapter->link_speed,
&adapter->link_duplex);
adapter->link_active = 1;
}
} else {
if (adapter->link_active == 1) {
adapter->link_speed = 0;
adapter->link_duplex = 0;
adapter->link_active = 0;
}
}
ifmr->ifm_status = IFM_AVALID;
ifmr->ifm_active = IFM_ETHER;
if (!adapter->link_active)
return;
ifmr->ifm_status |= IFM_ACTIVE;
if (adapter->hw.media_type == em_media_type_fiber) {
ifmr->ifm_active |= IFM_1000_SX | IFM_FDX;
} else {
switch (adapter->link_speed) {
case 10:
ifmr->ifm_active |= IFM_10_T;
break;
case 100:
ifmr->ifm_active |= IFM_100_TX;
break;
case 1000:
#if __FreeBSD_version < 500000
ifmr->ifm_active |= IFM_1000_TX;
#else
ifmr->ifm_active |= IFM_1000_T;
#endif
break;
}
if (adapter->link_duplex == FULL_DUPLEX)
ifmr->ifm_active |= IFM_FDX;
else
ifmr->ifm_active |= IFM_HDX;
}
return;
}
/*********************************************************************
*
* Media Ioctl callback
*
* This routine is called when the user changes speed/duplex using
* media/mediopt option with ifconfig.
*
**********************************************************************/
static int
em_media_change(struct ifnet *ifp)
{
struct adapter * adapter = ifp->if_softc;
struct ifmedia *ifm = &adapter->media;
INIT_DEBUGOUT("em_media_change: begin");
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
return(EINVAL);
switch (IFM_SUBTYPE(ifm->ifm_media)) {
case IFM_AUTO:
adapter->hw.autoneg = DO_AUTO_NEG;
adapter->hw.autoneg_advertised = AUTONEG_ADV_DEFAULT;
break;
case IFM_1000_SX:
#if __FreeBSD_version < 500000
case IFM_1000_TX:
#else
case IFM_1000_T:
#endif
adapter->hw.autoneg = DO_AUTO_NEG;
adapter->hw.autoneg_advertised = ADVERTISE_1000_FULL;
break;
case IFM_100_TX:
adapter->hw.autoneg = FALSE;
adapter->hw.autoneg_advertised = 0;
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
adapter->hw.forced_speed_duplex = em_100_full;
else
adapter->hw.forced_speed_duplex = em_100_half;
break;
case IFM_10_T:
adapter->hw.autoneg = FALSE;
adapter->hw.autoneg_advertised = 0;
if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX)
adapter->hw.forced_speed_duplex = em_10_full;
else
adapter->hw.forced_speed_duplex = em_10_half;
break;
default:
printf("em%d: Unsupported media type\n", adapter->unit);
}
/* As the speed/duplex settings my have changed we need to
* reset the PHY.
*/
adapter->hw.phy_reset_disable = FALSE;
em_init(adapter);
return(0);
}
static void
em_tx_cb(void *arg, bus_dma_segment_t *seg, int nsegs, bus_size_t mapsize, int error)
{
struct em_q *q = arg;
if (error)
return;
KASSERT(nsegs <= EM_MAX_SCATTER,
("Too many DMA segments returned when mapping tx packet"));
q->nsegs = nsegs;
bcopy(seg, q->segs, nsegs * sizeof(seg[0]));
}
/*********************************************************************
*
* This routine maps the mbufs to tx descriptors.
*
* return 0 on success, positive on failure
**********************************************************************/
static int
em_encap(struct adapter *adapter, struct mbuf **m_headp)
{
u_int32_t txd_upper;
u_int32_t txd_lower, txd_used = 0, txd_saved = 0;
int i, j, error;
u_int64_t address;
struct mbuf *m_head;
/* For 82544 Workaround */
DESC_ARRAY desc_array;
u_int32_t array_elements;
u_int32_t counter;
#if __FreeBSD_version < 500000
struct ifvlan *ifv = NULL;
#else
struct m_tag *mtag;
#endif
struct em_q q;
struct em_buffer *tx_buffer = NULL;
struct em_tx_desc *current_tx_desc = NULL;
struct ifnet *ifp = &adapter->interface_data.ac_if;
m_head = *m_headp;
/*
* Force a cleanup if number of TX descriptors
* available hits the threshold
*/
if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) {
em_clean_transmit_interrupts(adapter);
if (adapter->num_tx_desc_avail <= EM_TX_CLEANUP_THRESHOLD) {
adapter->no_tx_desc_avail1++;
return(ENOBUFS);
}
}
/*
* Map the packet for DMA.
*/
if (bus_dmamap_create(adapter->txtag, BUS_DMA_NOWAIT, &q.map)) {
adapter->no_tx_map_avail++;
return (ENOMEM);
}
error = bus_dmamap_load_mbuf(adapter->txtag, q.map,
m_head, em_tx_cb, &q, BUS_DMA_NOWAIT);
if (error != 0) {
adapter->no_tx_dma_setup++;
bus_dmamap_destroy(adapter->txtag, q.map);
return (error);
}
KASSERT(q.nsegs != 0, ("em_encap: empty packet"));
if (q.nsegs > adapter->num_tx_desc_avail) {
adapter->no_tx_desc_avail2++;
bus_dmamap_destroy(adapter->txtag, q.map);
return (ENOBUFS);
}
if (ifp->if_hwassist > 0) {
em_transmit_checksum_setup(adapter, m_head,
&txd_upper, &txd_lower);
} else
txd_upper = txd_lower = 0;
/* Find out if we are in vlan mode */
#if __FreeBSD_version < 500000
if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) &&
m_head->m_pkthdr.rcvif != NULL &&
m_head->m_pkthdr.rcvif->if_type == IFT_L2VLAN)
ifv = m_head->m_pkthdr.rcvif->if_softc;
#else
mtag = VLAN_OUTPUT_TAG(ifp, m_head);
#endif
/*
* When operating in promiscuous mode, hardware encapsulation for
* packets is disabled. This means we have to add the vlan
* encapsulation in the driver, since it will have come down from the
* VLAN layer with a tag instead of a VLAN header.
*/
if (mtag != NULL && adapter->em_insert_vlan_header) {
struct ether_vlan_header *evl;
struct ether_header eh;
m_head = m_pullup(m_head, sizeof(eh));
if (m_head == NULL) {
*m_headp = NULL;
bus_dmamap_destroy(adapter->txtag, q.map);
return (ENOBUFS);
}
eh = *mtod(m_head, struct ether_header *);
M_PREPEND(m_head, sizeof(*evl), M_DONTWAIT);
if (m_head == NULL) {
*m_headp = NULL;
bus_dmamap_destroy(adapter->txtag, q.map);
return (ENOBUFS);
}
m_head = m_pullup(m_head, sizeof(*evl));
if (m_head == NULL) {
*m_headp = NULL;
bus_dmamap_destroy(adapter->txtag, q.map);
return (ENOBUFS);
}
evl = mtod(m_head, struct ether_vlan_header *);
bcopy(&eh, evl, sizeof(*evl));
evl->evl_proto = evl->evl_encap_proto;
evl->evl_encap_proto = htons(ETHERTYPE_VLAN);
evl->evl_tag = htons(VLAN_TAG_VALUE(mtag));
m_tag_delete(m_head, mtag);
mtag = NULL;
*m_headp = m_head;
}
i = adapter->next_avail_tx_desc;
if (adapter->pcix_82544) {
txd_saved = i;
txd_used = 0;
}
for (j = 0; j < q.nsegs; j++) {
/* If adapter is 82544 and on PCIX bus */
if(adapter->pcix_82544) {
array_elements = 0;
address = htole64(q.segs[j].ds_addr);
/*
* Check the Address and Length combination and
* split the data accordingly
*/
array_elements = em_fill_descriptors(address,
htole32(q.segs[j].ds_len),
&desc_array);
for (counter = 0; counter < array_elements; counter++) {
if (txd_used == adapter->num_tx_desc_avail) {
adapter->next_avail_tx_desc = txd_saved;
adapter->no_tx_desc_avail2++;
bus_dmamap_destroy(adapter->txtag, q.map);
return (ENOBUFS);
}
tx_buffer = &adapter->tx_buffer_area[i];
current_tx_desc = &adapter->tx_desc_base[i];
current_tx_desc->buffer_addr = htole64(
desc_array.descriptor[counter].address);
current_tx_desc->lower.data = htole32(
(adapter->txd_cmd | txd_lower |
(u_int16_t)desc_array.descriptor[counter].length));
current_tx_desc->upper.data = htole32((txd_upper));
if (++i == adapter->num_tx_desc)
i = 0;
tx_buffer->m_head = NULL;
txd_used++;
}
} else {
tx_buffer = &adapter->tx_buffer_area[i];
current_tx_desc = &adapter->tx_desc_base[i];
current_tx_desc->buffer_addr = htole64(q.segs[j].ds_addr);
current_tx_desc->lower.data = htole32(
adapter->txd_cmd | txd_lower | q.segs[j].ds_len);
current_tx_desc->upper.data = htole32(txd_upper);
if (++i == adapter->num_tx_desc)
i = 0;
tx_buffer->m_head = NULL;
}
}
adapter->next_avail_tx_desc = i;
if (adapter->pcix_82544) {
adapter->num_tx_desc_avail -= txd_used;
}
else {
adapter->num_tx_desc_avail -= q.nsegs;
}
#if __FreeBSD_version < 500000
if (ifv != NULL) {
/* Set the vlan id */
current_tx_desc->upper.fields.special = htole16(ifv->ifv_tag);
#else
if (mtag != NULL) {
/* Set the vlan id */
current_tx_desc->upper.fields.special = htole16(VLAN_TAG_VALUE(mtag));
#endif
/* Tell hardware to add tag */
current_tx_desc->lower.data |= htole32(E1000_TXD_CMD_VLE);
}
tx_buffer->m_head = m_head;
tx_buffer->map = q.map;
bus_dmamap_sync(adapter->txtag, q.map, BUS_DMASYNC_PREWRITE);
/*
* Last Descriptor of Packet needs End Of Packet (EOP)
*/
current_tx_desc->lower.data |= htole32(E1000_TXD_CMD_EOP);
/*
* Advance the Transmit Descriptor Tail (Tdt), this tells the E1000
* that this frame is available to transmit.
*/
if (adapter->hw.mac_type == em_82547 &&
adapter->link_duplex == HALF_DUPLEX) {
em_82547_move_tail_locked(adapter);
} else {
E1000_WRITE_REG(&adapter->hw, TDT, i);
if (adapter->hw.mac_type == em_82547) {
em_82547_update_fifo_head(adapter, m_head->m_pkthdr.len);
}
}
return(0);
}
/*********************************************************************
*
* 82547 workaround to avoid controller hang in half-duplex environment.
* The workaround is to avoid queuing a large packet that would span
* the internal Tx FIFO ring boundary. We need to reset the FIFO pointers
* in this case. We do that only when FIFO is quiescent.
*
**********************************************************************/
static void
em_82547_move_tail_locked(struct adapter *adapter)
{
uint16_t hw_tdt;
uint16_t sw_tdt;
struct em_tx_desc *tx_desc;
uint16_t length = 0;
boolean_t eop = 0;
EM_LOCK_ASSERT(adapter);
hw_tdt = E1000_READ_REG(&adapter->hw, TDT);
sw_tdt = adapter->next_avail_tx_desc;
while (hw_tdt != sw_tdt) {
tx_desc = &adapter->tx_desc_base[hw_tdt];
length += tx_desc->lower.flags.length;
eop = tx_desc->lower.data & E1000_TXD_CMD_EOP;
if(++hw_tdt == adapter->num_tx_desc)
hw_tdt = 0;
if(eop) {
if (em_82547_fifo_workaround(adapter, length)) {
adapter->tx_fifo_wrk_cnt++;
callout_reset(&adapter->tx_fifo_timer, 1,
em_82547_move_tail, adapter);
break;
}
E1000_WRITE_REG(&adapter->hw, TDT, hw_tdt);
em_82547_update_fifo_head(adapter, length);
length = 0;
}
}
return;
}
static void
em_82547_move_tail(void *arg)
{
struct adapter *adapter = arg;