Impressum defxx.c   Sprache: C

 
/*
 * File Name:
 *   defxx.c
 *
 * Copyright Information:
 *   Copyright Digital Equipment Corporation 1996.
 *
 *   This software may be used and distributed according to the terms of
 *   the GNU General Public License, incorporated herein by reference.
 *
 * Abstract:
 *   A Linux device driver supporting the Digital Equipment Corporation
 *   FDDI TURBOchannel, EISA and PCI controller families.  Supported
 *   adapters include:
 *
 * DEC FDDIcontroller/TURBOchannel (DEFTA)
 * DEC FDDIcontroller/EISA         (DEFEA)
 * DEC FDDIcontroller/PCI          (DEFPA)
 *
 * The original author:
 *   LVS Lawrence V. Stefani <lstefani@yahoo.com>
 *
 * Maintainers:
 *   macro Maciej W. Rozycki <macro@orcam.me.uk>
 *
 * Credits:
 *   I'd like to thank Patricia Cross for helping me get started with
 *   Linux, David Davies for a lot of help upgrading and configuring
 *   my development system and for answering many OS and driver
 *   development questions, and Alan Cox for recommendations and
 *   integration help on getting FDDI support into Linux.  LVS
 *
 * Driver Architecture:
 *   The driver architecture is largely based on previous driver work
 *   for other operating systems.  The upper edge interface and
 *   functions were largely taken from existing Linux device drivers
 *   such as David Davies' DE4X5.C driver and Donald Becker's TULIP.C
 *   driver.
 *
 *   Adapter Probe -
 * The driver scans for supported EISA adapters by reading the
 * SLOT ID register for each EISA slot and making a match
 * against the expected value.
 *
 *   Bus-Specific Initialization -
 * This driver currently supports both EISA and PCI controller
 * families.  While the custom DMA chip and FDDI logic is similar
 * or identical, the bus logic is very different.  After
 * initialization, the only bus-specific differences is in how the
 * driver enables and disables interrupts.  Other than that, the
 * run-time critical code behaves the same on both families.
 * It's important to note that both adapter families are configured
 * to I/O map, rather than memory map, the adapter registers.
 *
 *   Driver Open/Close -
 * In the driver open routine, the driver ISR (interrupt service
 * routine) is registered and the adapter is brought to an
 * operational state.  In the driver close routine, the opposite
 * occurs; the driver ISR is deregistered and the adapter is
 * brought to a safe, but closed state.  Users may use consecutive
 * commands to bring the adapter up and down as in the following
 * example:
 * ifconfig fddi0 up
 * ifconfig fddi0 down
 * ifconfig fddi0 up
 *
 *   Driver Shutdown -
 * Apparently, there is no shutdown or halt routine support under
 * Linux.  This routine would be called during "reboot" or
 * "shutdown" to allow the driver to place the adapter in a safe
 * state before a warm reboot occurs.  To be really safe, the user
 * should close the adapter before shutdown (eg. ifconfig fddi0 down)
 * to ensure that the adapter DMA engine is taken off-line.  However,
 * the current driver code anticipates this problem and always issues
 * a soft reset of the adapter at the beginning of driver initialization.
 * A future driver enhancement in this area may occur in 2.1.X where
 * Alan indicated that a shutdown handler may be implemented.
 *
 *   Interrupt Service Routine -
 * The driver supports shared interrupts, so the ISR is registered for
 * each board with the appropriate flag and the pointer to that board's
 * device structure.  This provides the context during interrupt
 * processing to support shared interrupts and multiple boards.
 *
 * Interrupt enabling/disabling can occur at many levels.  At the host
 * end, you can disable system interrupts, or disable interrupts at the
 * PIC (on Intel systems).  Across the bus, both EISA and PCI adapters
 * have a bus-logic chip interrupt enable/disable as well as a DMA
 * controller interrupt enable/disable.
 *
 * The driver currently enables and disables adapter interrupts at the
 * bus-logic chip and assumes that Linux will take care of clearing or
 * acknowledging any host-based interrupt chips.
 *
 *   Control Functions -
 * Control functions are those used to support functions such as adding
 * or deleting multicast addresses, enabling or disabling packet
 * reception filters, or other custom/proprietary commands.  Presently,
 * the driver supports the "get statistics", "set multicast list", and
 * "set mac address" functions defined by Linux.  A list of possible
 * enhancements include:
 *
 * - Custom ioctl interface for executing port interface commands
 * - Custom ioctl interface for adding unicast addresses to
 *   adapter CAM (to support bridge functions).
 * - Custom ioctl interface for supporting firmware upgrades.
 *
 *   Hardware (port interface) Support Routines -
 * The driver function names that start with "dfx_hw_" represent
 * low-level port interface routines that are called frequently.  They
 * include issuing a DMA or port control command to the adapter,
 * resetting the adapter, or reading the adapter state.  Since the
 * driver initialization and run-time code must make calls into the
 * port interface, these routines were written to be as generic and
 * usable as possible.
 *
 *   Receive Path -
 * The adapter DMA engine supports a 256 entry receive descriptor block
 * of which up to 255 entries can be used at any given time.  The
 * architecture is a standard producer, consumer, completion model in
 * which the driver "produces" receive buffers to the adapter, the
 * adapter "consumes" the receive buffers by DMAing incoming packet data,
 * and the driver "completes" the receive buffers by servicing the
 * incoming packet, then "produces" a new buffer and starts the cycle
 * again.  Receive buffers can be fragmented in up to 16 fragments
 * (descriptor entries).  For simplicity, this driver posts
 * single-fragment receive buffers of 4608 bytes, then allocates a
 * sk_buff, copies the data, then reposts the buffer.  To reduce CPU
 * utilization, a better approach would be to pass up the receive
 * buffer (no extra copy) then allocate and post a replacement buffer.
 * This is a performance enhancement that should be looked into at
 * some point.
 *
 *   Transmit Path -
 * Like the receive path, the adapter DMA engine supports a 256 entry
 * transmit descriptor block of which up to 255 entries can be used at
 * any given time.  Transmit buffers can be fragmented in up to 255
 * fragments (descriptor entries).  This driver always posts one
 * fragment per transmit packet request.
 *
 * The fragment contains the entire packet from FC to end of data.
 * Before posting the buffer to the adapter, the driver sets a three-byte
 * packet request header (PRH) which is required by the Motorola MAC chip
 * used on the adapters.  The PRH tells the MAC the type of token to
 * receive/send, whether or not to generate and append the CRC, whether
 * synchronous or asynchronous framing is used, etc.  Since the PRH
 * definition is not necessarily consistent across all FDDI chipsets,
 * the driver, rather than the common FDDI packet handler routines,
 * sets these bytes.
 *
 * To reduce the amount of descriptor fetches needed per transmit request,
 * the driver takes advantage of the fact that there are at least three
 * bytes available before the skb->data field on the outgoing transmit
 * request.  This is guaranteed by having fddi_setup() in net_init.c set
 * dev->hard_header_len to 24 bytes.  21 bytes accounts for the largest
 * header in an 802.2 SNAP frame.  The other 3 bytes are the extra "pad"
 * bytes which we'll use to store the PRH.
 *
 * There's a subtle advantage to adding these pad bytes to the
 * hard_header_len, it ensures that the data portion of the packet for
 * an 802.2 SNAP frame is longword aligned.  Other FDDI driver
 * implementations may not need the extra padding and can start copying
 * or DMAing directly from the FC byte which starts at skb->data.  Should
 * another driver implementation need ADDITIONAL padding, the net_init.c
 * module should be updated and dev->hard_header_len should be increased.
 * NOTE: To maintain the alignment on the data portion of the packet,
 * dev->hard_header_len should always be evenly divisible by 4 and at
 * least 24 bytes in size.
 *
 * Modification History:
 * Date Name Description
 * 16-Aug-96 LVS Created.
 * 20-Aug-96 LVS Updated dfx_probe so that version information
 * string is only displayed if 1 or more cards are
 * found.  Changed dfx_rcv_queue_process to copy
 * 3 NULL bytes before FC to ensure that data is
 * longword aligned in receive buffer.
 * 09-Sep-96 LVS Updated dfx_ctl_set_multicast_list to enable
 * LLC group promiscuous mode if multicast list
 * is too large.  LLC individual/group promiscuous
 * mode is now disabled if IFF_PROMISC flag not set.
 * dfx_xmt_queue_pkt no longer checks for NULL skb
 * on Alan Cox recommendation.  Added node address
 * override support.
 * 12-Sep-96 LVS Reset current address to factory address during
 * device open.  Updated transmit path to post a
 * single fragment which includes PRH->end of data.
 * Mar 2000 AC Did various cleanups for 2.3.x
 * Jun 2000 jgarzik PCI and resource alloc cleanups
 * Jul 2000 tjeerd Much cleanup and some bug fixes
 * Sep 2000 tjeerd Fix leak on unload, cosmetic code cleanup
 * Feb 2001 Skb allocation fixes
 * Feb 2001 davej PCI enable cleanups.
 * 04 Aug 2003 macro Converted to the DMA API.
 * 14 Aug 2004 macro Fix device names reported.
 * 14 Jun 2005 macro Use irqreturn_t.
 * 23 Oct 2006 macro Big-endian host support.
 * 14 Dec 2006 macro TURBOchannel support.
 * 01 Jul 2014 macro Fixes for DMA on 64-bit hosts.
 * 10 Mar 2021 macro Dynamic MMIO vs port I/O.
 */


/* Include files */
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/eisa.h>
#include <linux/errno.h>
#include <linux/fddidevice.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/tc.h>

#include <asm/byteorder.h>
#include <asm/io.h>

#include "defxx.h"

/* Version information string should be updated prior to each new release!  */
#define DRV_NAME "defxx"
#define DRV_VERSION "v1.12"
#define DRV_RELDATE "2021/03/10"

static const char version[] =
 DRV_NAME ": " DRV_VERSION " " DRV_RELDATE
 " Lawrence V. Stefani and others\n";

#define DYNAMIC_BUFFERS 1

#define SKBUFF_RX_COPYBREAK 200
/*
 * NEW_SKB_SIZE = PI_RCV_DATA_K_SIZE_MAX+128 to allow 128 byte
 * alignment for compatibility with old EISA boards.
 */

#define NEW_SKB_SIZE (PI_RCV_DATA_K_SIZE_MAX+128)

#ifdef CONFIG_EISA
#define DFX_BUS_EISA(dev) (dev->bus == &eisa_bus_type)
#else
#define DFX_BUS_EISA(dev) 0
#endif

#ifdef CONFIG_TC
#define DFX_BUS_TC(dev) (dev->bus == &tc_bus_type)
#else
#define DFX_BUS_TC(dev) 0
#endif

#ifdef CONFIG_HAS_IOPORT
#define dfx_use_mmio bp->mmio
#else
#define dfx_use_mmio true
#endif

/* Define module-wide (static) routines */

static void  dfx_bus_init(struct net_device *dev);
static void  dfx_bus_uninit(struct net_device *dev);
static void  dfx_bus_config_check(DFX_board_t *bp);

static int  dfx_driver_init(struct net_device *dev,
     const char *print_name,
     resource_size_t bar_start);
static int  dfx_adap_init(DFX_board_t *bp, int get_buffers);

static int  dfx_open(struct net_device *dev);
static int  dfx_close(struct net_device *dev);

static void  dfx_int_pr_halt_id(DFX_board_t *bp);
static void  dfx_int_type_0_process(DFX_board_t *bp);
static void  dfx_int_common(struct net_device *dev);
static irqreturn_t dfx_interrupt(int irq, void *dev_id);

static struct  net_device_stats *dfx_ctl_get_stats(struct net_device *dev);
static void  dfx_ctl_set_multicast_list(struct net_device *dev);
static int  dfx_ctl_set_mac_address(struct net_device *dev, void *addr);
static int  dfx_ctl_update_cam(DFX_board_t *bp);
static int  dfx_ctl_update_filters(DFX_board_t *bp);

static int  dfx_hw_dma_cmd_req(DFX_board_t *bp);
static int  dfx_hw_port_ctrl_req(DFX_board_t *bp, PI_UINT32 command, PI_UINT32 data_a, PI_UINT32 data_b, PI_UINT32 *host_data);
static void  dfx_hw_adap_reset(DFX_board_t *bp, PI_UINT32 type);
static int  dfx_hw_adap_state_rd(DFX_board_t *bp);
static int  dfx_hw_dma_uninit(DFX_board_t *bp, PI_UINT32 type);

static int  dfx_rcv_init(DFX_board_t *bp, int get_buffers);
static void  dfx_rcv_queue_process(DFX_board_t *bp);
#ifdef DYNAMIC_BUFFERS
static void  dfx_rcv_flush(DFX_board_t *bp);
#else
static inline void dfx_rcv_flush(DFX_board_t *bp) {}
#endif

static netdev_tx_t dfx_xmt_queue_pkt(struct sk_buff *skb,
         struct net_device *dev);
static int  dfx_xmt_done(DFX_board_t *bp);
static void  dfx_xmt_flush(DFX_board_t *bp);

/* Define module-wide (static) variables */

static struct pci_driver dfx_pci_driver;
static struct eisa_driver dfx_eisa_driver;
static struct tc_driver dfx_tc_driver;


/*
 * =======================
 * = dfx_port_write_long =
 * = dfx_port_read_long  =
 * =======================
 *
 * Overview:
 *   Routines for reading and writing values from/to adapter
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *   offset - register offset from base I/O address
 *   data - for dfx_port_write_long, this is a value to write;
 *   for dfx_port_read_long, this is a pointer to store
 *   the read value
 *
 * Functional Description:
 *   These routines perform the correct operation to read or write
 *   the adapter register.
 *
 *   EISA port block base addresses are based on the slot number in which the
 *   controller is installed.  For example, if the EISA controller is installed
 *   in slot 4, the port block base address is 0x4000.  If the controller is
 *   installed in slot 2, the port block base address is 0x2000, and so on.
 *   This port block can be used to access PDQ, ESIC, and DEFEA on-board
 *   registers using the register offsets defined in DEFXX.H.
 *
 *   PCI port block base addresses are assigned by the PCI BIOS or system
 *   firmware.  There is one 128 byte port block which can be accessed.  It
 *   allows for I/O mapping of both PDQ and PFI registers using the register
 *   offsets defined in DEFXX.H.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   bp->base is a valid base I/O address for this adapter.
 *   offset is a valid register offset for this adapter.
 *
 * Side Effects:
 *   Rather than produce macros for these functions, these routines
 *   are defined using "inline" to ensure that the compiler will
 *   generate inline code and not waste a procedure call and return.
 *   This provides all the benefits of macros, but with the
 *   advantage of strict data type checking.
 */


static inline void dfx_writel(DFX_board_t *bp, int offset, u32 data)
{
 writel(data, bp->base.mem + offset);
 mb();
}

static inline void dfx_outl(DFX_board_t *bp, int offset, u32 data)
{
 outl(data, bp->base.port + offset);
}

static void dfx_port_write_long(DFX_board_t *bp, int offset, u32 data)
{
 struct device __maybe_unused *bdev = bp->bus_dev;

 if (dfx_use_mmio)
  dfx_writel(bp, offset, data);
 else
  dfx_outl(bp, offset, data);
}


static inline void dfx_readl(DFX_board_t *bp, int offset, u32 *data)
{
 mb();
 *data = readl(bp->base.mem + offset);
}

static inline void dfx_inl(DFX_board_t *bp, int offset, u32 *data)
{
 *data = inl(bp->base.port + offset);
}

static void dfx_port_read_long(DFX_board_t *bp, int offset, u32 *data)
{
 struct device __maybe_unused *bdev = bp->bus_dev;

 if (dfx_use_mmio)
  dfx_readl(bp, offset, data);
 else
  dfx_inl(bp, offset, data);
}


/*
 * ================
 * = dfx_get_bars =
 * ================
 *
 * Overview:
 *   Retrieves the address ranges used to access control and status
 *   registers.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *   bar_start - pointer to store the start addresses
 *   bar_len - pointer to store the lengths of the areas
 *
 * Assumptions:
 *   I am sure there are some.
 *
 * Side Effects:
 *   None
 */

static void dfx_get_bars(DFX_board_t *bp,
    resource_size_t *bar_start, resource_size_t *bar_len)
{
 struct device *bdev = bp->bus_dev;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 int dfx_bus_tc = DFX_BUS_TC(bdev);

 if (dfx_bus_pci) {
  int num = dfx_use_mmio ? 0 : 1;

  bar_start[0] = pci_resource_start(to_pci_dev(bdev), num);
  bar_len[0] = pci_resource_len(to_pci_dev(bdev), num);
  bar_start[2] = bar_start[1] = 0;
  bar_len[2] = bar_len[1] = 0;
 }
 if (dfx_bus_eisa) {
  unsigned long base_addr = to_eisa_device(bdev)->base_addr;
  resource_size_t bar_lo;
  resource_size_t bar_hi;

  if (dfx_use_mmio) {
   bar_lo = inb(base_addr + PI_ESIC_K_MEM_ADD_LO_CMP_2);
   bar_lo <<= 8;
   bar_lo |= inb(base_addr + PI_ESIC_K_MEM_ADD_LO_CMP_1);
   bar_lo <<= 8;
   bar_lo |= inb(base_addr + PI_ESIC_K_MEM_ADD_LO_CMP_0);
   bar_lo <<= 8;
   bar_start[0] = bar_lo;
   bar_hi = inb(base_addr + PI_ESIC_K_MEM_ADD_HI_CMP_2);
   bar_hi <<= 8;
   bar_hi |= inb(base_addr + PI_ESIC_K_MEM_ADD_HI_CMP_1);
   bar_hi <<= 8;
   bar_hi |= inb(base_addr + PI_ESIC_K_MEM_ADD_HI_CMP_0);
   bar_hi <<= 8;
   bar_len[0] = ((bar_hi - bar_lo) | PI_MEM_ADD_MASK_M) +
         1;
  } else {
   bar_start[0] = base_addr;
   bar_len[0] = PI_ESIC_K_CSR_IO_LEN;
  }
  bar_start[1] = base_addr + PI_DEFEA_K_BURST_HOLDOFF;
  bar_len[1] = PI_ESIC_K_BURST_HOLDOFF_LEN;
  bar_start[2] = base_addr + PI_ESIC_K_ESIC_CSR;
  bar_len[2] = PI_ESIC_K_ESIC_CSR_LEN;
 }
 if (dfx_bus_tc) {
  bar_start[0] = to_tc_dev(bdev)->resource.start +
          PI_TC_K_CSR_OFFSET;
  bar_len[0] = PI_TC_K_CSR_LEN;
  bar_start[2] = bar_start[1] = 0;
  bar_len[2] = bar_len[1] = 0;
 }
}

static const struct net_device_ops dfx_netdev_ops = {
 .ndo_open  = dfx_open,
 .ndo_stop  = dfx_close,
 .ndo_start_xmit  = dfx_xmt_queue_pkt,
 .ndo_get_stats  = dfx_ctl_get_stats,
 .ndo_set_rx_mode = dfx_ctl_set_multicast_list,
 .ndo_set_mac_address = dfx_ctl_set_mac_address,
};

static void dfx_register_res_err(const char *print_name, bool mmio,
     unsigned long start, unsigned long len)
{
 pr_err("%s: Cannot reserve %s resource 0x%lx @ 0x%lx, aborting\n",
        print_name, mmio ? "MMIO" : "I/O", len, start);
}

/*
 * ================
 * = dfx_register =
 * ================
 *
 * Overview:
 *   Initializes a supported FDDI controller
 *
 * Returns:
 *   Condition code
 *
 * Arguments:
 *   bdev - pointer to device information
 *
 * Functional Description:
 *
 * Return Codes:
 *   0  - This device (fddi0, fddi1, etc) configured successfully
 *   -EBUSY      - Failed to get resources, or dfx_driver_init failed.
 *
 * Assumptions:
 *   It compiles so it should work :-( (PCI cards do :-)
 *
 * Side Effects:
 *   Device structures for FDDI adapters (fddi0, fddi1, etc) are
 *   initialized and the board resources are read and stored in
 *   the device structure.
 */

static int dfx_register(struct device *bdev)
{
 static int version_disp;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 const char *print_name = dev_name(bdev);
 struct net_device *dev;
 DFX_board_t   *bp;   /* board pointer */
 resource_size_t bar_start[3] = {0}; /* pointers to ports */
 resource_size_t bar_len[3] = {0}; /* resource length */
 int alloc_size;    /* total buffer size used */
 struct resource *region;
 int err = 0;

 if (!version_disp) { /* display version info if adapter is found */
  version_disp = 1; /* set display flag to TRUE so that */
  printk(version); /* we only display this string ONCE */
 }

 dev = alloc_fddidev(sizeof(*bp));
 if (!dev) {
  printk(KERN_ERR "%s: Unable to allocate fddidev, aborting\n",
         print_name);
  return -ENOMEM;
 }

 /* Enable PCI device. */
 if (dfx_bus_pci) {
  err = pci_enable_device(to_pci_dev(bdev));
  if (err) {
   pr_err("%s: Cannot enable PCI device, aborting\n",
          print_name);
   goto err_out;
  }
 }

 SET_NETDEV_DEV(dev, bdev);

 bp = netdev_priv(dev);
 bp->bus_dev = bdev;
 dev_set_drvdata(bdev, dev);

 bp->mmio = true;

 dfx_get_bars(bp, bar_start, bar_len);
 if (bar_len[0] == 0 ||
     (dfx_bus_eisa && dfx_use_mmio && bar_start[0] == 0)) {
  bp->mmio = false;
  dfx_get_bars(bp, bar_start, bar_len);
 }

 if (dfx_use_mmio) {
  region = request_mem_region(bar_start[0], bar_len[0],
         bdev->driver->name);
  if (!region && (dfx_bus_eisa || dfx_bus_pci)) {
   bp->mmio = false;
   dfx_get_bars(bp, bar_start, bar_len);
  }
 }
 if (!dfx_use_mmio)
  region = request_region(bar_start[0], bar_len[0],
     bdev->driver->name);
 if (!region) {
  dfx_register_res_err(print_name, dfx_use_mmio,
         bar_start[0], bar_len[0]);
  err = -EBUSY;
  goto err_out_disable;
 }
 if (bar_start[1] != 0) {
  region = request_region(bar_start[1], bar_len[1],
     bdev->driver->name);
  if (!region) {
   dfx_register_res_err(print_name, 0,
          bar_start[1], bar_len[1]);
   err = -EBUSY;
   goto err_out_csr_region;
  }
 }
 if (bar_start[2] != 0) {
  region = request_region(bar_start[2], bar_len[2],
     bdev->driver->name);
  if (!region) {
   dfx_register_res_err(print_name, 0,
          bar_start[2], bar_len[2]);
   err = -EBUSY;
   goto err_out_bh_region;
  }
 }

 /* Set up I/O base address. */
 if (dfx_use_mmio) {
  bp->base.mem = ioremap(bar_start[0], bar_len[0]);
  if (!bp->base.mem) {
   printk(KERN_ERR "%s: Cannot map MMIO\n", print_name);
   err = -ENOMEM;
   goto err_out_esic_region;
  }
 } else {
  bp->base.port = bar_start[0];
  dev->base_addr = bar_start[0];
 }

 /* Initialize new device structure */
 dev->netdev_ops   = &dfx_netdev_ops;

 if (dfx_bus_pci)
  pci_set_master(to_pci_dev(bdev));

 if (dfx_driver_init(dev, print_name, bar_start[0]) != DFX_K_SUCCESS) {
  err = -ENODEV;
  goto err_out_unmap;
 }

 err = register_netdev(dev);
 if (err)
  goto err_out_kfree;

 printk("%s: registered as %s\n", print_name, dev->name);
 return 0;

err_out_kfree:
 alloc_size = sizeof(PI_DESCR_BLOCK) +
       PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX +
#ifndef DYNAMIC_BUFFERS
       (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) +
#endif
       sizeof(PI_CONSUMER_BLOCK) +
       (PI_ALIGN_K_DESC_BLK - 1);
 if (bp->kmalloced)
  dma_free_coherent(bdev, alloc_size,
      bp->kmalloced, bp->kmalloced_dma);

err_out_unmap:
 if (dfx_use_mmio)
  iounmap(bp->base.mem);

err_out_esic_region:
 if (bar_start[2] != 0)
  release_region(bar_start[2], bar_len[2]);

err_out_bh_region:
 if (bar_start[1] != 0)
  release_region(bar_start[1], bar_len[1]);

err_out_csr_region:
 if (dfx_use_mmio)
  release_mem_region(bar_start[0], bar_len[0]);
 else
  release_region(bar_start[0], bar_len[0]);

err_out_disable:
 if (dfx_bus_pci)
  pci_disable_device(to_pci_dev(bdev));

err_out:
 free_netdev(dev);
 return err;
}


/*
 * ================
 * = dfx_bus_init =
 * ================
 *
 * Overview:
 *   Initializes the bus-specific controller logic.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   dev - pointer to device information
 *
 * Functional Description:
 *   Determine and save adapter IRQ in device table,
 *   then perform bus-specific logic initialization.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   bp->base has already been set with the proper
 *  base I/O address for this device.
 *
 * Side Effects:
 *   Interrupts are enabled at the adapter bus-specific logic.
 *   Note:  Interrupts at the DMA engine (PDQ chip) are not
 *   enabled yet.
 */


static void dfx_bus_init(struct net_device *dev)
{
 DFX_board_t *bp = netdev_priv(dev);
 struct device *bdev = bp->bus_dev;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 int dfx_bus_tc = DFX_BUS_TC(bdev);
 u8 val;

 DBG_printk("In dfx_bus_init...\n");

 /* Initialize a pointer back to the net_device struct */
 bp->dev = dev;

 /* Initialize adapter based on bus type */

 if (dfx_bus_tc)
  dev->irq = to_tc_dev(bdev)->interrupt;
 if (dfx_bus_eisa) {
  unsigned long base_addr = to_eisa_device(bdev)->base_addr;

  /* Disable the board before fiddling with the decoders.  */
  outb(0, base_addr + PI_ESIC_K_SLOT_CNTRL);

  /* Get the interrupt level from the ESIC chip.  */
  val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
  val &= PI_CONFIG_STAT_0_M_IRQ;
  val >>= PI_CONFIG_STAT_0_V_IRQ;

  switch (val) {
  case PI_CONFIG_STAT_0_IRQ_K_9:
   dev->irq = 9;
   break;

  case PI_CONFIG_STAT_0_IRQ_K_10:
   dev->irq = 10;
   break;

  case PI_CONFIG_STAT_0_IRQ_K_11:
   dev->irq = 11;
   break;

  case PI_CONFIG_STAT_0_IRQ_K_15:
   dev->irq = 15;
   break;
  }

  /*
 * Enable memory decoding (MEMCS1) and/or port decoding
 * (IOCS1/IOCS0) as appropriate in Function Control
 * Register.  MEMCS1 or IOCS0 is used for PDQ registers,
 * taking 16 32-bit words, while IOCS1 is used for the
 * Burst Holdoff register, taking a single 32-bit word
 * only.  We use the slot-specific I/O range as per the
 * ESIC spec, that is set bits 15:12 in the mask registers
 * to mask them out.
 */


  /* Set the decode range of the board.  */
  val = 0;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_0_1);
  val = PI_DEFEA_K_CSR_IO;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_0_0);

  val = PI_IO_CMP_M_SLOT;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_0_1);
  val = (PI_ESIC_K_CSR_IO_LEN - 1) & ~3;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_0_0);

  val = 0;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_1_1);
  val = PI_DEFEA_K_BURST_HOLDOFF;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_CMP_1_0);

  val = PI_IO_CMP_M_SLOT;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_1_1);
  val = (PI_ESIC_K_BURST_HOLDOFF_LEN - 1) & ~3;
  outb(val, base_addr + PI_ESIC_K_IO_ADD_MASK_1_0);

  /* Enable the decoders.  */
  val = PI_FUNCTION_CNTRL_M_IOCS1;
  if (dfx_use_mmio)
   val |= PI_FUNCTION_CNTRL_M_MEMCS1;
  else
   val |= PI_FUNCTION_CNTRL_M_IOCS0;
  outb(val, base_addr + PI_ESIC_K_FUNCTION_CNTRL);

  /*
 * Enable access to the rest of the module
 * (including PDQ and packet memory).
 */

  val = PI_SLOT_CNTRL_M_ENB;
  outb(val, base_addr + PI_ESIC_K_SLOT_CNTRL);

  /*
 * Map PDQ registers into memory or port space.  This is
 * done with a bit in the Burst Holdoff register.
 */

  val = inb(base_addr + PI_DEFEA_K_BURST_HOLDOFF);
  if (dfx_use_mmio)
   val |= PI_BURST_HOLDOFF_M_MEM_MAP;
  else
   val &= ~PI_BURST_HOLDOFF_M_MEM_MAP;
  outb(val, base_addr + PI_DEFEA_K_BURST_HOLDOFF);

  /* Enable interrupts at EISA bus interface chip (ESIC) */
  val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
  val |= PI_CONFIG_STAT_0_M_INT_ENB;
  outb(val, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
 }
 if (dfx_bus_pci) {
  struct pci_dev *pdev = to_pci_dev(bdev);

  /* Get the interrupt level from the PCI Configuration Table */

  dev->irq = pdev->irq;

  /* Check Latency Timer and set if less than minimal */

  pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &val);
  if (val < PFI_K_LAT_TIMER_MIN) {
   val = PFI_K_LAT_TIMER_DEF;
   pci_write_config_byte(pdev, PCI_LATENCY_TIMER, val);
  }

  /* Enable interrupts at PCI bus interface chip (PFI) */
  val = PFI_MODE_M_PDQ_INT_ENB | PFI_MODE_M_DMA_ENB;
  dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, val);
 }
}

/*
 * ==================
 * = dfx_bus_uninit =
 * ==================
 *
 * Overview:
 *   Uninitializes the bus-specific controller logic.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   dev - pointer to device information
 *
 * Functional Description:
 *   Perform bus-specific logic uninitialization.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   bp->base has already been set with the proper
 *  base I/O address for this device.
 *
 * Side Effects:
 *   Interrupts are disabled at the adapter bus-specific logic.
 */


static void dfx_bus_uninit(struct net_device *dev)
{
 DFX_board_t *bp = netdev_priv(dev);
 struct device *bdev = bp->bus_dev;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 u8 val;

 DBG_printk("In dfx_bus_uninit...\n");

 /* Uninitialize adapter based on bus type */

 if (dfx_bus_eisa) {
  unsigned long base_addr = to_eisa_device(bdev)->base_addr;

  /* Disable interrupts at EISA bus interface chip (ESIC) */
  val = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
  val &= ~PI_CONFIG_STAT_0_M_INT_ENB;
  outb(val, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);

  /* Disable the board.  */
  outb(0, base_addr + PI_ESIC_K_SLOT_CNTRL);

  /* Disable memory and port decoders.  */
  outb(0, base_addr + PI_ESIC_K_FUNCTION_CNTRL);
 }
 if (dfx_bus_pci) {
  /* Disable interrupts at PCI bus interface chip (PFI) */
  dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL, 0);
 }
}


/*
 * ========================
 * = dfx_bus_config_check =
 * ========================
 *
 * Overview:
 *   Checks the configuration (burst size, full-duplex, etc.)  If any parameters
 *   are illegal, then this routine will set new defaults.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *
 * Functional Description:
 *   For Revision 1 FDDI EISA, Revision 2 or later FDDI EISA with rev E or later
 *   PDQ, and all FDDI PCI controllers, all values are legal.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   dfx_adap_init has NOT been called yet so burst size and other items have
 *   not been set.
 *
 * Side Effects:
 *   None
 */


static void dfx_bus_config_check(DFX_board_t *bp)
{
 struct device __maybe_unused *bdev = bp->bus_dev;
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 int status;    /* return code from adapter port control call */
 u32 host_data;   /* LW data returned from port control call */

 DBG_printk("In dfx_bus_config_check...\n");

 /* Configuration check only valid for EISA adapter */

 if (dfx_bus_eisa) {
  /*
 * First check if revision 2 EISA controller.  Rev. 1 cards used
 * PDQ revision B, so no workaround needed in this case.  Rev. 3
 * cards used PDQ revision E, so no workaround needed in this
 * case, either.  Only Rev. 2 cards used either Rev. D or E
 * chips, so we must verify the chip revision on Rev. 2 cards.
 */

  if (to_eisa_device(bdev)->id.driver_data == DEFEA_PROD_ID_2) {
   /*
 * Revision 2 FDDI EISA controller found,
 * so let's check PDQ revision of adapter.
 */

   status = dfx_hw_port_ctrl_req(bp,
           PI_PCTRL_M_SUB_CMD,
           PI_SUB_CMD_K_PDQ_REV_GET,
           0,
           &host_data);
   if ((status != DFX_K_SUCCESS) || (host_data == 2))
    {
    /*
 * Either we couldn't determine the PDQ revision, or
 * we determined that it is at revision D.  In either case,
 * we need to implement the workaround.
 */


    /* Ensure that the burst size is set to 8 longwords or less */

    switch (bp->burst_size)
     {
     case PI_PDATA_B_DMA_BURST_SIZE_32:
     case PI_PDATA_B_DMA_BURST_SIZE_16:
      bp->burst_size = PI_PDATA_B_DMA_BURST_SIZE_8;
      break;

     default:
      break;
     }

    /* Ensure that full-duplex mode is not enabled */

    bp->full_duplex_enb = PI_SNMP_K_FALSE;
    }
   }
  }
 }


/*
 * ===================
 * = dfx_driver_init =
 * ===================
 *
 * Overview:
 *   Initializes remaining adapter board structure information
 *   and makes sure adapter is in a safe state prior to dfx_open().
 *
 * Returns:
 *   Condition code
 *
 * Arguments:
 *   dev - pointer to device information
 *   print_name - printable device name
 *
 * Functional Description:
 *   This function allocates additional resources such as the host memory
 *   blocks needed by the adapter (eg. descriptor and consumer blocks).
 *  Remaining bus initialization steps are also completed.  The adapter
 *   is also reset so that it is in the DMA_UNAVAILABLE state.  The OS
 *   must call dfx_open() to open the adapter and bring it on-line.
 *
 * Return Codes:
 *   DFX_K_SUCCESS - initialization succeeded
 *   DFX_K_FAILURE - initialization failed - could not allocate memory
 * or read adapter MAC address
 *
 * Assumptions:
 *   Memory allocated from dma_alloc_coherent() call is physically
 *   contiguous, locked memory.
 *
 * Side Effects:
 *   Adapter is reset and should be in DMA_UNAVAILABLE state before
 *   returning from this routine.
 */


static int dfx_driver_init(struct net_device *dev, const char *print_name,
      resource_size_t bar_start)
{
 DFX_board_t *bp = netdev_priv(dev);
 struct device *bdev = bp->bus_dev;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 int dfx_bus_tc = DFX_BUS_TC(bdev);
 int alloc_size;   /* total buffer size needed */
 char *top_v, *curr_v;  /* virtual addrs into memory block */
 dma_addr_t top_p, curr_p; /* physical addrs into memory block */
 u32 data;   /* host data register value */
 __le32 le32;
 char *board_name = NULL;

 DBG_printk("In dfx_driver_init...\n");

 /* Initialize bus-specific hardware registers */

 dfx_bus_init(dev);

 /*
 * Initialize default values for configurable parameters
 *
 * Note: All of these parameters are ones that a user may
 *       want to customize.  It'd be nice to break these
 *  out into Space.c or someplace else that's more
 *  accessible/understandable than this file.
 */


 bp->full_duplex_enb  = PI_SNMP_K_FALSE;
 bp->req_ttrt   = 8 * 12500;  /* 8ms in 80 nanosec units */
 bp->burst_size   = PI_PDATA_B_DMA_BURST_SIZE_DEF;
 bp->rcv_bufs_to_post = RCV_BUFS_DEF;

 /*
 * Ensure that HW configuration is OK
 *
 * Note: Depending on the hardware revision, we may need to modify
 *       some of the configurable parameters to workaround hardware
 *       limitations.  We'll perform this configuration check AFTER
 *       setting the parameters to their default values.
 */


 dfx_bus_config_check(bp);

 /* Disable PDQ interrupts first */

 dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS);

 /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */

 (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST);

 /*  Read the factory MAC address from the adapter then save it */

 if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_LO, 0,
     &data) != DFX_K_SUCCESS) {
  printk("%s: Could not read adapter factory MAC address!\n",
         print_name);
  return DFX_K_FAILURE;
 }
 le32 = cpu_to_le32(data);
 memcpy(&bp->factory_mac_addr[0], &le32, sizeof(u32));

 if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_HI, 0,
     &data) != DFX_K_SUCCESS) {
  printk("%s: Could not read adapter factory MAC address!\n",
         print_name);
  return DFX_K_FAILURE;
 }
 le32 = cpu_to_le32(data);
 memcpy(&bp->factory_mac_addr[4], &le32, sizeof(u16));

 /*
 * Set current address to factory address
 *
 * Note: Node address override support is handled through
 *       dfx_ctl_set_mac_address.
 */


 dev_addr_set(dev, bp->factory_mac_addr);
 if (dfx_bus_tc)
  board_name = "DEFTA";
 if (dfx_bus_eisa)
  board_name = "DEFEA";
 if (dfx_bus_pci)
  board_name = "DEFPA";
 pr_info("%s: %s at %s addr = 0x%llx, IRQ = %d, Hardware addr = %pMF\n",
  print_name, board_name, dfx_use_mmio ? "MMIO" : "I/O",
  (long long)bar_start, dev->irq, dev->dev_addr);

 /*
 * Get memory for descriptor block, consumer block, and other buffers
 * that need to be DMA read or written to by the adapter.
 */


 alloc_size = sizeof(PI_DESCR_BLOCK) +
     PI_CMD_REQ_K_SIZE_MAX +
     PI_CMD_RSP_K_SIZE_MAX +
#ifndef DYNAMIC_BUFFERS
     (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) +
#endif
     sizeof(PI_CONSUMER_BLOCK) +
     (PI_ALIGN_K_DESC_BLK - 1);
 bp->kmalloced = top_v = dma_alloc_coherent(bp->bus_dev, alloc_size,
         &bp->kmalloced_dma,
         GFP_ATOMIC);
 if (top_v == NULL)
  return DFX_K_FAILURE;

 top_p = bp->kmalloced_dma; /* get physical address of buffer */

 /*
 *  To guarantee the 8K alignment required for the descriptor block, 8K - 1
 *  plus the amount of memory needed was allocated.  The physical address
 * is now 8K aligned.  By carving up the memory in a specific order,
 *  we'll guarantee the alignment requirements for all other structures.
 *
 *  Note: If the assumptions change regarding the non-paged, non-cached,
 *   physically contiguous nature of the memory block or the address
 *   alignments, then we'll need to implement a different algorithm
 *   for allocating the needed memory.
 */


 curr_p = ALIGN(top_p, PI_ALIGN_K_DESC_BLK);
 curr_v = top_v + (curr_p - top_p);

 /* Reserve space for descriptor block */

 bp->descr_block_virt = (PI_DESCR_BLOCK *) curr_v;
 bp->descr_block_phys = curr_p;
 curr_v += sizeof(PI_DESCR_BLOCK);
 curr_p += sizeof(PI_DESCR_BLOCK);

 /* Reserve space for command request buffer */

 bp->cmd_req_virt = (PI_DMA_CMD_REQ *) curr_v;
 bp->cmd_req_phys = curr_p;
 curr_v += PI_CMD_REQ_K_SIZE_MAX;
 curr_p += PI_CMD_REQ_K_SIZE_MAX;

 /* Reserve space for command response buffer */

 bp->cmd_rsp_virt = (PI_DMA_CMD_RSP *) curr_v;
 bp->cmd_rsp_phys = curr_p;
 curr_v += PI_CMD_RSP_K_SIZE_MAX;
 curr_p += PI_CMD_RSP_K_SIZE_MAX;

 /* Reserve space for the LLC host receive queue buffers */

 bp->rcv_block_virt = curr_v;
 bp->rcv_block_phys = curr_p;

#ifndef DYNAMIC_BUFFERS
 curr_v += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX);
 curr_p += (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX);
#endif

 /* Reserve space for the consumer block */

 bp->cons_block_virt = (PI_CONSUMER_BLOCK *) curr_v;
 bp->cons_block_phys = curr_p;

 /* Display virtual and physical addresses if debug driver */

 DBG_printk("%s: Descriptor block virt = %p, phys = %pad\n",
     print_name, bp->descr_block_virt, &bp->descr_block_phys);
 DBG_printk("%s: Command Request buffer virt = %p, phys = %pad\n",
     print_name, bp->cmd_req_virt, &bp->cmd_req_phys);
 DBG_printk("%s: Command Response buffer virt = %p, phys = %pad\n",
     print_name, bp->cmd_rsp_virt, &bp->cmd_rsp_phys);
 DBG_printk("%s: Receive buffer block virt = %p, phys = %pad\n",
     print_name, bp->rcv_block_virt, &bp->rcv_block_phys);
 DBG_printk("%s: Consumer block virt = %p, phys = %pad\n",
     print_name, bp->cons_block_virt, &bp->cons_block_phys);

 return DFX_K_SUCCESS;
}


/*
 * =================
 * = dfx_adap_init =
 * =================
 *
 * Overview:
 *   Brings the adapter to the link avail/link unavailable state.
 *
 * Returns:
 *   Condition code
 *
 * Arguments:
 *   bp - pointer to board information
 *   get_buffers - non-zero if buffers to be allocated
 *
 * Functional Description:
 *   Issues the low-level firmware/hardware calls necessary to bring
 *   the adapter up, or to properly reset and restore adapter during
 *   run-time.
 *
 * Return Codes:
 *   DFX_K_SUCCESS - Adapter brought up successfully
 *   DFX_K_FAILURE - Adapter initialization failed
 *
 * Assumptions:
 *   bp->reset_type should be set to a valid reset type value before
 *   calling this routine.
 *
 * Side Effects:
 *   Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state
 *   upon a successful return of this routine.
 */


static int dfx_adap_init(DFX_board_t *bp, int get_buffers)
 {
 DBG_printk("In dfx_adap_init...\n");

 /* Disable PDQ interrupts first */

 dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS);

 /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */

 if (dfx_hw_dma_uninit(bp, bp->reset_type) != DFX_K_SUCCESS)
  {
  printk("%s: Could not uninitialize/reset adapter!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /*
 * When the PDQ is reset, some false Type 0 interrupts may be pending,
 * so we'll acknowledge all Type 0 interrupts now before continuing.
 */


 dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, PI_HOST_INT_K_ACK_ALL_TYPE_0);

 /*
 * Clear Type 1 and Type 2 registers before going to DMA_AVAILABLE state
 *
 * Note: We only need to clear host copies of these registers.  The PDQ reset
 *       takes care of the on-board register values.
 */


 bp->cmd_req_reg.lword = 0;
 bp->cmd_rsp_reg.lword = 0;
 bp->rcv_xmt_reg.lword = 0;

 /* Clear consumer block before going to DMA_AVAILABLE state */

 memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK));

 /* Initialize the DMA Burst Size */

 if (dfx_hw_port_ctrl_req(bp,
       PI_PCTRL_M_SUB_CMD,
       PI_SUB_CMD_K_BURST_SIZE_SET,
       bp->burst_size,
       NULL) != DFX_K_SUCCESS)
  {
  printk("%s: Could not set adapter burst size!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /*
 * Set base address of Consumer Block
 *
 * Assumption: 32-bit physical address of consumer block is 64 byte
 *    aligned.  That is, bits 0-5 of the address must be zero.
 */


 if (dfx_hw_port_ctrl_req(bp,
       PI_PCTRL_M_CONS_BLOCK,
       bp->cons_block_phys,
       0,
       NULL) != DFX_K_SUCCESS)
  {
  printk("%s: Could not set consumer block address!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /*
 * Set the base address of Descriptor Block and bring adapter
 * to DMA_AVAILABLE state.
 *
 * Note: We also set the literal and data swapping requirements
 *       in this command.
 *
 * Assumption: 32-bit physical address of descriptor block
 *       is 8Kbyte aligned.
 */

 if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_INIT,
     (u32)(bp->descr_block_phys |
           PI_PDATA_A_INIT_M_BSWAP_INIT),
     0, NULL) != DFX_K_SUCCESS) {
  printk("%s: Could not set descriptor block address!\n",
         bp->dev->name);
  return DFX_K_FAILURE;
 }

 /* Set transmit flush timeout value */

 bp->cmd_req_virt->cmd_type = PI_CMD_K_CHARS_SET;
 bp->cmd_req_virt->char_set.item[0].item_code = PI_ITEM_K_FLUSH_TIME;
 bp->cmd_req_virt->char_set.item[0].value  = 3; /* 3 seconds */
 bp->cmd_req_virt->char_set.item[0].item_index = 0;
 bp->cmd_req_virt->char_set.item[1].item_code = PI_ITEM_K_EOL;
 if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS)
  {
  printk("%s: DMA command request failed!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /* Set the initial values for eFDXEnable and MACTReq MIB objects */

 bp->cmd_req_virt->cmd_type = PI_CMD_K_SNMP_SET;
 bp->cmd_req_virt->snmp_set.item[0].item_code = PI_ITEM_K_FDX_ENB_DIS;
 bp->cmd_req_virt->snmp_set.item[0].value  = bp->full_duplex_enb;
 bp->cmd_req_virt->snmp_set.item[0].item_index = 0;
 bp->cmd_req_virt->snmp_set.item[1].item_code = PI_ITEM_K_MAC_T_REQ;
 bp->cmd_req_virt->snmp_set.item[1].value  = bp->req_ttrt;
 bp->cmd_req_virt->snmp_set.item[1].item_index = 0;
 bp->cmd_req_virt->snmp_set.item[2].item_code = PI_ITEM_K_EOL;
 if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS)
  {
  printk("%s: DMA command request failed!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /* Initialize adapter CAM */

 if (dfx_ctl_update_cam(bp) != DFX_K_SUCCESS)
  {
  printk("%s: Adapter CAM update failed!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /* Initialize adapter filters */

 if (dfx_ctl_update_filters(bp) != DFX_K_SUCCESS)
  {
  printk("%s: Adapter filters update failed!\n", bp->dev->name);
  return DFX_K_FAILURE;
  }

 /*
 * Remove any existing dynamic buffers (i.e. if the adapter is being
 * reinitialized)
 */


 if (get_buffers)
  dfx_rcv_flush(bp);

 /* Initialize receive descriptor block and produce buffers */

 if (dfx_rcv_init(bp, get_buffers))
         {
  printk("%s: Receive buffer allocation failed\n", bp->dev->name);
  if (get_buffers)
   dfx_rcv_flush(bp);
  return DFX_K_FAILURE;
  }

 /* Issue START command and bring adapter to LINK_(UN)AVAILABLE state */

 bp->cmd_req_virt->cmd_type = PI_CMD_K_START;
 if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS)
  {
  printk("%s: Start command failed\n", bp->dev->name);
  if (get_buffers)
   dfx_rcv_flush(bp);
  return DFX_K_FAILURE;
  }

 /* Initialization succeeded, reenable PDQ interrupts */

 dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_ENABLE_DEF_INTS);
 return DFX_K_SUCCESS;
 }


/*
 * ============
 * = dfx_open =
 * ============
 *
 * Overview:
 *   Opens the adapter
 *
 * Returns:
 *   Condition code
 *
 * Arguments:
 *   dev - pointer to device information
 *
 * Functional Description:
 *   This function brings the adapter to an operational state.
 *
 * Return Codes:
 *   0  - Adapter was successfully opened
 *   -EAGAIN - Could not register IRQ or adapter initialization failed
 *
 * Assumptions:
 *   This routine should only be called for a device that was
 *   initialized successfully.
 *
 * Side Effects:
 *   Adapter should be in LINK_AVAILABLE or LINK_UNAVAILABLE state
 *   if the open is successful.
 */


static int dfx_open(struct net_device *dev)
{
 DFX_board_t *bp = netdev_priv(dev);
 int ret;

 DBG_printk("In dfx_open...\n");

 /* Register IRQ - support shared interrupts by passing device ptr */

 ret = request_irq(dev->irq, dfx_interrupt, IRQF_SHARED, dev->name,
     dev);
 if (ret) {
  printk(KERN_ERR "%s: Requested IRQ %d is busy\n", dev->name, dev->irq);
  return ret;
 }

 /*
 * Set current address to factory MAC address
 *
 * Note: We've already done this step in dfx_driver_init.
 *       However, it's possible that a user has set a node
 *  address override, then closed and reopened the
 *  adapter.  Unless we reset the device address field
 *  now, we'll continue to use the existing modified
 *  address.
 */


 dev_addr_set(dev, bp->factory_mac_addr);

 /* Clear local unicast/multicast address tables and counts */

 memset(bp->uc_table, 0, sizeof(bp->uc_table));
 memset(bp->mc_table, 0, sizeof(bp->mc_table));
 bp->uc_count = 0;
 bp->mc_count = 0;

 /* Disable promiscuous filter settings */

 bp->ind_group_prom = PI_FSTATE_K_BLOCK;
 bp->group_prom  = PI_FSTATE_K_BLOCK;

 spin_lock_init(&bp->lock);

 /* Reset and initialize adapter */

 bp->reset_type = PI_PDATA_A_RESET_M_SKIP_ST; /* skip self-test */
 if (dfx_adap_init(bp, 1) != DFX_K_SUCCESS)
 {
  printk(KERN_ERR "%s: Adapter open failed!\n", dev->name);
  free_irq(dev->irq, dev);
  return -EAGAIN;
 }

 /* Set device structure info */
 netif_start_queue(dev);
 return 0;
}


/*
 * =============
 * = dfx_close =
 * =============
 *
 * Overview:
 *   Closes the device/module.
 *
 * Returns:
 *   Condition code
 *
 * Arguments:
 *   dev - pointer to device information
 *
 * Functional Description:
 *   This routine closes the adapter and brings it to a safe state.
 *   The interrupt service routine is deregistered with the OS.
 *   The adapter can be opened again with another call to dfx_open().
 *
 * Return Codes:
 *   Always return 0.
 *
 * Assumptions:
 *   No further requests for this adapter are made after this routine is
 *   called.  dfx_open() can be called to reset and reinitialize the
 *   adapter.
 *
 * Side Effects:
 *   Adapter should be in DMA_UNAVAILABLE state upon completion of this
 *   routine.
 */


static int dfx_close(struct net_device *dev)
{
 DFX_board_t *bp = netdev_priv(dev);

 DBG_printk("In dfx_close...\n");

 /* Disable PDQ interrupts first */

 dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS);

 /* Place adapter in DMA_UNAVAILABLE state by resetting adapter */

 (void) dfx_hw_dma_uninit(bp, PI_PDATA_A_RESET_M_SKIP_ST);

 /*
 * Flush any pending transmit buffers
 *
 * Note: It's important that we flush the transmit buffers
 *  BEFORE we clear our copy of the Type 2 register.
 *  Otherwise, we'll have no idea how many buffers
 *  we need to free.
 */


 dfx_xmt_flush(bp);

 /*
 * Clear Type 1 and Type 2 registers after adapter reset
 *
 * Note: Even though we're closing the adapter, it's
 *       possible that an interrupt will occur after
 *  dfx_close is called.  Without some assurance to
 *  the contrary we want to make sure that we don't
 *  process receive and transmit LLC frames and update
 *  the Type 2 register with bad information.
 */


 bp->cmd_req_reg.lword = 0;
 bp->cmd_rsp_reg.lword = 0;
 bp->rcv_xmt_reg.lword = 0;

 /* Clear consumer block for the same reason given above */

 memset(bp->cons_block_virt, 0, sizeof(PI_CONSUMER_BLOCK));

 /* Release all dynamically allocate skb in the receive ring. */

 dfx_rcv_flush(bp);

 /* Clear device structure flags */

 netif_stop_queue(dev);

 /* Deregister (free) IRQ */

 free_irq(dev->irq, dev);

 return 0;
}


/*
 * ======================
 * = dfx_int_pr_halt_id =
 * ======================
 *
 * Overview:
 *   Displays halt id's in string form.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *
 * Functional Description:
 *   Determine current halt id and display appropriate string.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   None
 *
 * Side Effects:
 *   None
 */


static void dfx_int_pr_halt_id(DFX_board_t *bp)
 {
 PI_UINT32 port_status;   /* PDQ port status register value */
 PI_UINT32 halt_id;    /* PDQ port status halt ID */

 /* Read the latest port status */

 dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status);

 /* Display halt state transition information */

 halt_id = (port_status & PI_PSTATUS_M_HALT_ID) >> PI_PSTATUS_V_HALT_ID;
 switch (halt_id)
  {
  case PI_HALT_ID_K_SELFTEST_TIMEOUT:
   printk("%s: Halt ID: Selftest Timeout\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_PARITY_ERROR:
   printk("%s: Halt ID: Host Bus Parity Error\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_HOST_DIR_HALT:
   printk("%s: Halt ID: Host-Directed Halt\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_SW_FAULT:
   printk("%s: Halt ID: Adapter Software Fault\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_HW_FAULT:
   printk("%s: Halt ID: Adapter Hardware Fault\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_PC_TRACE:
   printk("%s: Halt ID: FDDI Network PC Trace Path Test\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_DMA_ERROR:
   printk("%s: Halt ID: Adapter DMA Error\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_IMAGE_CRC_ERROR:
   printk("%s: Halt ID: Firmware Image CRC Error\n", bp->dev->name);
   break;

  case PI_HALT_ID_K_BUS_EXCEPTION:
   printk("%s: Halt ID: 68000 Bus Exception\n", bp->dev->name);
   break;

  default:
   printk("%s: Halt ID: Unknown (code = %X)\n", bp->dev->name, halt_id);
   break;
  }
 }


/*
 * ==========================
 * = dfx_int_type_0_process =
 * ==========================
 *
 * Overview:
 *   Processes Type 0 interrupts.
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *
 * Functional Description:
 *   Processes all enabled Type 0 interrupts.  If the reason for the interrupt
 *   is a serious fault on the adapter, then an error message is displayed
 *   and the adapter is reset.
 *
 *   One tricky potential timing window is the rapid succession of "link avail"
 *   "link unavail" state change interrupts.  The acknowledgement of the Type 0
 *   interrupt must be done before reading the state from the Port Status
 *   register.  This is true because a state change could occur after reading
 *   the data, but before acknowledging the interrupt.  If this state change
 *   does happen, it would be lost because the driver is using the old state,
 *   and it will never know about the new state because it subsequently
 *   acknowledges the state change interrupt.
 *
 *          INCORRECT                                      CORRECT
 *      read type 0 int reasons                   read type 0 int reasons
 *      read adapter state                        ack type 0 interrupts
 *      ack type 0 interrupts                     read adapter state
 *      ... process interrupt ...                 ... process interrupt ...
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   None
 *
 * Side Effects:
 *   An adapter reset may occur if the adapter has any Type 0 error interrupts
 *   or if the port status indicates that the adapter is halted.  The driver
 *   is responsible for reinitializing the adapter with the current CAM
 *   contents and adapter filter settings.
 */


static void dfx_int_type_0_process(DFX_board_t *bp)

 {
 PI_UINT32 type_0_status;  /* Host Interrupt Type 0 register */
 PI_UINT32 state;    /* current adap state (from port status) */

 /*
 * Read host interrupt Type 0 register to determine which Type 0
 * interrupts are pending.  Immediately write it back out to clear
 * those interrupts.
 */


 dfx_port_read_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, &type_0_status);
 dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_0_STATUS, type_0_status);

 /* Check for Type 0 error interrupts */

 if (type_0_status & (PI_TYPE_0_STAT_M_NXM |
       PI_TYPE_0_STAT_M_PM_PAR_ERR |
       PI_TYPE_0_STAT_M_BUS_PAR_ERR))
  {
  /* Check for Non-Existent Memory error */

  if (type_0_status & PI_TYPE_0_STAT_M_NXM)
   printk("%s: Non-Existent Memory Access Error\n", bp->dev->name);

  /* Check for Packet Memory Parity error */

  if (type_0_status & PI_TYPE_0_STAT_M_PM_PAR_ERR)
   printk("%s: Packet Memory Parity Error\n", bp->dev->name);

  /* Check for Host Bus Parity error */

  if (type_0_status & PI_TYPE_0_STAT_M_BUS_PAR_ERR)
   printk("%s: Host Bus Parity Error\n", bp->dev->name);

  /* Reset adapter and bring it back on-line */

  bp->link_available = PI_K_FALSE; /* link is no longer available */
  bp->reset_type = 0;     /* rerun on-board diagnostics */
  printk("%s: Resetting adapter...\n", bp->dev->name);
  if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS)
   {
   printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name);
   dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS);
   return;
   }
  printk("%s: Adapter reset successful!\n", bp->dev->name);
  return;
  }

 /* Check for transmit flush interrupt */

 if (type_0_status & PI_TYPE_0_STAT_M_XMT_FLUSH)
  {
  /* Flush any pending xmt's and acknowledge the flush interrupt */

  bp->link_available = PI_K_FALSE;  /* link is no longer available */
  dfx_xmt_flush(bp);      /* flush any outstanding packets */
  (void) dfx_hw_port_ctrl_req(bp,
         PI_PCTRL_M_XMT_DATA_FLUSH_DONE,
         0,
         0,
         NULL);
  }

 /* Check for adapter state change */

 if (type_0_status & PI_TYPE_0_STAT_M_STATE_CHANGE)
  {
  /* Get latest adapter state */

  state = dfx_hw_adap_state_rd(bp); /* get adapter state */
  if (state == PI_STATE_K_HALTED)
   {
   /*
 * Adapter has transitioned to HALTED state, try to reset
 * adapter to bring it back on-line.  If reset fails,
 * leave the adapter in the broken state.
 */


   printk("%s: Controller has transitioned to HALTED state!\n", bp->dev->name);
   dfx_int_pr_halt_id(bp);   /* display halt id as string */

   /* Reset adapter and bring it back on-line */

   bp->link_available = PI_K_FALSE; /* link is no longer available */
   bp->reset_type = 0;     /* rerun on-board diagnostics */
   printk("%s: Resetting adapter...\n", bp->dev->name);
   if (dfx_adap_init(bp, 0) != DFX_K_SUCCESS)
    {
    printk("%s: Adapter reset failed! Disabling adapter interrupts.\n", bp->dev->name);
    dfx_port_write_long(bp, PI_PDQ_K_REG_HOST_INT_ENB, PI_HOST_INT_K_DISABLE_ALL_INTS);
    return;
    }
   printk("%s: Adapter reset successful!\n", bp->dev->name);
   }
  else if (state == PI_STATE_K_LINK_AVAIL)
   {
   bp->link_available = PI_K_TRUE;  /* set link available flag */
   }
  }
 }


/*
 * ==================
 * = dfx_int_common =
 * ==================
 *
 * Overview:
 *   Interrupt service routine (ISR)
 *
 * Returns:
 *   None
 *
 * Arguments:
 *   bp - pointer to board information
 *
 * Functional Description:
 *   This is the ISR which processes incoming adapter interrupts.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   This routine assumes PDQ interrupts have not been disabled.
 *   When interrupts are disabled at the PDQ, the Port Status register
 *   is automatically cleared.  This routine uses the Port Status
 *   register value to determine whether a Type 0 interrupt occurred,
 *   so it's important that adapter interrupts are not normally
 *   enabled/disabled at the PDQ.
 *
 *   It's vital that this routine is NOT reentered for the
 *   same board and that the OS is not in another section of
 *   code (eg. dfx_xmt_queue_pkt) for the same board on a
 *   different thread.
 *
 * Side Effects:
 *   Pending interrupts are serviced.  Depending on the type of
 *   interrupt, acknowledging and clearing the interrupt at the
 *   PDQ involves writing a register to clear the interrupt bit
 *   or updating completion indices.
 */


static void dfx_int_common(struct net_device *dev)
{
 DFX_board_t *bp = netdev_priv(dev);
 PI_UINT32 port_status;  /* Port Status register */

 /* Process xmt interrupts - frequent case, so always call this routine */

 if(dfx_xmt_done(bp))    /* free consumed xmt packets */
  netif_wake_queue(dev);

 /* Process rcv interrupts - frequent case, so always call this routine */

 dfx_rcv_queue_process(bp);  /* service received LLC frames */

 /*
 * Transmit and receive producer and completion indices are updated on the
 * adapter by writing to the Type 2 Producer register.  Since the frequent
 * case is that we'll be processing either LLC transmit or receive buffers,
 * we'll optimize I/O writes by doing a single register write here.
 */


 dfx_port_write_long(bp, PI_PDQ_K_REG_TYPE_2_PROD, bp->rcv_xmt_reg.lword);

 /* Read PDQ Port Status register to find out which interrupts need processing */

 dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &port_status);

 /* Process Type 0 interrupts (if any) - infrequent, so only call when needed */

 if (port_status & PI_PSTATUS_M_TYPE_0_PENDING)
  dfx_int_type_0_process(bp); /* process Type 0 interrupts */
 }


/*
 * =================
 * = dfx_interrupt =
 * =================
 *
 * Overview:
 *   Interrupt processing routine
 *
 * Returns:
 *   Whether a valid interrupt was seen.
 *
 * Arguments:
 *   irq - interrupt vector
 *   dev_id - pointer to device information
 *
 * Functional Description:
 *   This routine calls the interrupt processing routine for this adapter.  It
 *   disables and reenables adapter interrupts, as appropriate.  We can support
 *   shared interrupts since the incoming dev_id pointer provides our device
 *   structure context.
 *
 * Return Codes:
 *   IRQ_HANDLED - an IRQ was handled.
 *   IRQ_NONE    - no IRQ was handled.
 *
 * Assumptions:
 *   The interrupt acknowledgement at the hardware level (eg. ACKing the PIC
 *   on Intel-based systems) is done by the operating system outside this
 *   routine.
 *
 *  System interrupts are enabled through this call.
 *
 * Side Effects:
 *   Interrupts are disabled, then reenabled at the adapter.
 */


static irqreturn_t dfx_interrupt(int irq, void *dev_id)
{
 struct net_device *dev = dev_id;
 DFX_board_t *bp = netdev_priv(dev);
 struct device *bdev = bp->bus_dev;
 int dfx_bus_pci = dev_is_pci(bdev);
 int dfx_bus_eisa = DFX_BUS_EISA(bdev);
 int dfx_bus_tc = DFX_BUS_TC(bdev);

 /* Service adapter interrupts */

 if (dfx_bus_pci) {
  u32 status;

  dfx_port_read_long(bp, PFI_K_REG_STATUS, &status);
  if (!(status & PFI_STATUS_M_PDQ_INT))
   return IRQ_NONE;

  spin_lock(&bp->lock);

  /* Disable PDQ-PFI interrupts at PFI */
  dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL,
        PFI_MODE_M_DMA_ENB);

  /* Call interrupt service routine for this adapter */
  dfx_int_common(dev);

  /* Clear PDQ interrupt status bit and reenable interrupts */
  dfx_port_write_long(bp, PFI_K_REG_STATUS,
        PFI_STATUS_M_PDQ_INT);
  dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL,
        (PFI_MODE_M_PDQ_INT_ENB |
         PFI_MODE_M_DMA_ENB));

  spin_unlock(&bp->lock);
 }
 if (dfx_bus_eisa) {
  unsigned long base_addr = to_eisa_device(bdev)->base_addr;
  u8 status;

  status = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
  if (!(status & PI_CONFIG_STAT_0_M_PEND))
   return IRQ_NONE;

  spin_lock(&bp->lock);

  /* Disable interrupts at the ESIC */
  status &= ~PI_CONFIG_STAT_0_M_INT_ENB;
  outb(status, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);

  /* Call interrupt service routine for this adapter */
  dfx_int_common(dev);

  /* Reenable interrupts at the ESIC */
  status = inb(base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);
  status |= PI_CONFIG_STAT_0_M_INT_ENB;
  outb(status, base_addr + PI_ESIC_K_IO_CONFIG_STAT_0);

  spin_unlock(&bp->lock);
 }
 if (dfx_bus_tc) {
  u32 status;

  dfx_port_read_long(bp, PI_PDQ_K_REG_PORT_STATUS, &status);
  if (!(status & (PI_PSTATUS_M_RCV_DATA_PENDING |
    PI_PSTATUS_M_XMT_DATA_PENDING |
    PI_PSTATUS_M_SMT_HOST_PENDING |
    PI_PSTATUS_M_UNSOL_PENDING |
    PI_PSTATUS_M_CMD_RSP_PENDING |
    PI_PSTATUS_M_CMD_REQ_PENDING |
    PI_PSTATUS_M_TYPE_0_PENDING)))
   return IRQ_NONE;

  spin_lock(&bp->lock);

  /* Call interrupt service routine for this adapter */
  dfx_int_common(dev);

  spin_unlock(&bp->lock);
 }

 return IRQ_HANDLED;
}


/*
 * =====================
 * = dfx_ctl_get_stats =
 * =====================
 *
 * Overview:
 *   Get statistics for FDDI adapter
 *
 * Returns:
 *   Pointer to FDDI statistics structure
 *
 * Arguments:
 *   dev - pointer to device information
 *
 * Functional Description:
 *   Gets current MIB objects from adapter, then
 *   returns FDDI statistics structure as defined
 *   in if_fddi.h.
 *
 *   Note: Since the FDDI statistics structure is
 *   still new and the device structure doesn't
 *   have an FDDI-specific get statistics handler,
 *   we'll return the FDDI statistics structure as
 *   a pointer to an Ethernet statistics structure.
 *   That way, at least the first part of the statistics
 *   structure can be decoded properly, and it allows
 *   "smart" applications to perform a second cast to
 *   decode the FDDI-specific statistics.
 *
 *   We'll have to pay attention to this routine as the
 *   device structure becomes more mature and LAN media
 *   independent.
 *
 * Return Codes:
 *   None
 *
 * Assumptions:
 *   None
 *
 * Side Effects:
 *   None
 */


static struct net_device_stats *dfx_ctl_get_stats(struct net_device *dev)
 {
 DFX_board_t *bp = netdev_priv(dev);

 /* Fill the bp->stats structure with driver-maintained counters */

 bp->stats.gen.rx_packets = bp->rcv_total_frames;
 bp->stats.gen.tx_packets = bp->xmt_total_frames;
 bp->stats.gen.rx_bytes   = bp->rcv_total_bytes;
 bp->stats.gen.tx_bytes   = bp->xmt_total_bytes;
 bp->stats.gen.rx_errors  = bp->rcv_crc_errors +
       bp->rcv_frame_status_errors +
       bp->rcv_length_errors;
 bp->stats.gen.tx_errors  = bp->xmt_length_errors;
 bp->stats.gen.rx_dropped = bp->rcv_discards;
 bp->stats.gen.tx_dropped = bp->xmt_discards;
 bp->stats.gen.multicast  = bp->rcv_multicast_frames;
 bp->stats.gen.collisions = 0;  /* always zero (0) for FDDI */

 /* Get FDDI SMT MIB objects */

 bp->cmd_req_virt->cmd_type = PI_CMD_K_SMT_MIB_GET;
 if (dfx_hw_dma_cmd_req(bp) != DFX_K_SUCCESS)
  return (struct net_device_stats *)&bp->stats;

 /* Fill the bp->stats structure with the SMT MIB object values */

 memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id));
 bp->stats.smt_op_version_id     = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id;
 bp->stats.smt_hi_version_id     = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id;
 bp->stats.smt_lo_version_id     = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id;
 memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data));
 bp->stats.smt_mib_version_id    = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id;
 bp->stats.smt_mac_cts      = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct;
 bp->stats.smt_non_master_cts    = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct;
 bp->stats.smt_master_cts     = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct;
 bp->stats.smt_available_paths    = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths;
 bp->stats.smt_config_capabilities   = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities;
 bp->stats.smt_config_policy     = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy;
 bp->stats.smt_connection_policy    = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy;
 bp->stats.smt_t_notify      = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify;
 bp->stats.smt_stat_rpt_policy    = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy;
 bp->stats.smt_trace_max_expiration   = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration;
 bp->stats.smt_bypass_present    = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present;
 bp->stats.smt_ecm_state      = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state;
 bp->stats.smt_cf_state      = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state;
 bp->stats.smt_remote_disconnect_flag  = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag;
 bp->stats.smt_station_status    = bp->cmd_rsp_virt->smt_mib_get.smt_station_status;
 bp->stats.smt_peer_wrap_flag    = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag;
 bp->stats.smt_time_stamp     = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls;
 bp->stats.smt_transition_time_stamp   = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls;
 bp->stats.mac_frame_status_functions  = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions;
 bp->stats.mac_t_max_capability    = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability;
 bp->stats.mac_tvx_capability    = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability;
 bp->stats.mac_available_paths    = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths;
 bp->stats.mac_current_path     = bp->cmd_rsp_virt->smt_mib_get.mac_current_path;
 memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=93 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.61Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge