Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

chip_intel_82557_cardbus.c

/* $Id: chip_intel_82557_cardbus.c,v 1.18 2009-01-28 12:59:18 potyra Exp $ 
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

/* FIXME merge changes here with PCI version, split into different arch files */
#define __CARDBUS__     1

#include "config.h"
#include <inttypes.h>

#include <fcntl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>

#include "qemu/bswap.h"
#include "glue-main.h"
#include "glue-log.h"
#include "glue-shm.h"

#include "pci.h"

#include "chip_intel_82557_cardbus.h"

#define COMP      "chip_intel_82557_cardbus"


/* TODO LIST:
 * - if possible (in other words, if documentation can be found): Make the
 *   FlashROM actually flashable.
 * - implement CRC
 * - fix (? does it need to be fixed?) flexible mode
 * - fix stuff so the diag tool is happy
 * - (?) implement pci_reset - depends on: PCI
 */

/* Debugging stuff */
#define WARNINGS 0
#define DEBUGMASK 0x0000
/* Add the following values together to select which debug messages you want
 * to see */
#define DEBUG_CONFSPACE       0x0001
#define DEBUG_EEPROM          0x0002
#define DEBUG_INTGEN          0x0004
#define DEBUG_MDI       0x0008
#define DEBUG_OTHER           0x0010
#define DEBUG_PORTREADS       0x0020
#define DEBUG_FLASHROM        0x0040
#define DEBUG_DUMPACKETS      0x0080

/* Happy little defines... because, in our world, an eepro100 has a
 * lot of features, it needs a lot of defines too.
 */

/* How many bytes of our mapped memory are actually useful (i.e. used by
   registers) */
#define SZ_E100MEMORY   0x40

/* How much Flash ROM we have. (Always 64K on 82557) */
#define SZ_E100FLASHROM (64*1024)

/* How much space we REQUEST for ROM (1 MB on 82557) */
#define SZ_E100ROMREQUEST (1024*1024)

/* SCB System Control Block, Offset 0x00 - 0x07
 * |-SCB Status Word,        Offset 0x00 - 0x01
 * | |-SCB Status Byte,      Offset 0x00
 * | \-STAT/ACK byte,        Offset 0x01
 * |-SCB Command Word,       Offset 0x02 - 0x03
 * | |-Command Byte,         Offset 0x02
 * | \-Interrupt Control B., Offset 0x03
 * \-SCB General Pointer,    Offset 0x04 - 0x07
 */
#define SCB_STATUS_CUS  0xC0  /* CU (Command Unit) status */
#define SCB_STATUS_CUS_SHIFT  6
#define SCB_STATUS_RUS  0x3C  /* RU (Receive Unit) status */
#define SCB_STATUS_RUS_SHIFT  2
#define CUSTATUS(x)     ((x & SCB_STATUS_CUS) >> SCB_STATUS_CUS_SHIFT)
#define RUSTATUS(x)     ((x & SCB_STATUS_RUS) >> SCB_STATUS_RUS_SHIFT)

#define SCB_STATACK_CX  0x80  /* The CU has finished executing a command */
#define SCB_STATACK_FR  0x40  /* The RU has finished receiving a frame */
#define SCB_STATACK_CNA 0x20  /* The CU has left the active state */
#define SCB_STATACK_RNR 0x10  /* The RU has left the ready state */
#define SCB_STATACK_MDI 0x08  /* MDI R or W cycle has completed */
#define SCB_STATACK_SWI 0x04  /* Software generated interrupt */

#define SCB_CMD_CUC     0xF0  /* CU Command */
#define SCB_CMD_CUC_SHIFT  4
#define SCB_CMD_RUC     0x07  /* RU Command */
#define SCB_CMD_RUC_SHIFT  0
#define CUCMD(x) ((x & SCB_CMD_CUC) >> SCB_CMD_CUC_SHIFT)
#define RUCMD(x) ((x & SCB_CMD_RUC) >> SCB_CMD_RUC_SHIFT)

#define SCB_ICB_SI      0x02  /* Software generated Interrupt */
#define SCB_ICB_M 0x01  /* Interrupt Mask Bit */

/* Status Codes for RUS */
#define RUS_IDLE  0     /* Idle */
#define RUS_SUSPENDED   1     /* Suspended */
#define RUS_NORES 2     /* No resources (EVIL!) */
#define RUS_READY 4     /* Ready */
/* Status Codes for CUS */
#define CUS_IDLE  0     /* Idle */
#define CUS_SUSPENDED   1     /* Suspended */
#define CUS_LPQACT      2     /* LPQ Active */
#define CUS_HQPACT      3     /* HQP Active */

/* the PORT register,  Offset 0x08 - 0x0B */
#define PORT_OPCODEMASK 0x0000000F /* which bits of the port register are */
                           /* the opcode (rest is address) */

/* Reserved, Offset 0x0C - 0x0D */

/* EEPROM Interface, Offset 0x0E - 0x0F */
#define EEPROM_EEDO     8     /* EEPROM Serial Data Out */
#define EEPROM_EEDI     4     /* EEPROM Serial Data In */
#define EEPROM_EECS     2     /* EEPROM Chip Select */
#define EEPROM_EESK     1     /* EEPROM Serial Clock */
/* Macros for getting the values of some signal (0 or 1) */
#define EEPROM_DOSIGNAL(x)    ((x & EEPROM_EEDO) > 0)
#define EEPROM_DISIGNAL(x)    ((x & EEPROM_EEDI) > 0)
#define EEPROM_CSSIGNAL(x)    ((x & EEPROM_EECS) > 0)
#define EEPROM_SKSIGNAL(x)    ((x & EEPROM_EESK) > 0)

#define EEPROM_OPCODEBITS      3    /* How many bits an eeprom opcode */
                              /* has (always 3) */
#define EEPROM_ADDRESSBITS     6    /* How many bits an eeprom address */
                              /* has (6 on 82557*/
#define SZ_E100EEPROM   64

/* MDI Management Data Interface, Offset 0x10 - 0x13 */
#define MDI_DATA  0x0000FFFF  /* The data bits */
#define MDI_REGAD 0x001F0000  /* PHY Register Address */
#define MDI_REGAD_SHIFT       16
#define MDI_PHYAD 0x03E00000  /* PHY Address */
#define MDI_PHYAD_SHIFT       21
#define MDI_OPCODE      0x0C000000  /* MDI Opcode */
#define MDI_OPCODE_SHIFT      26    /* How Far we have to shift the Opcode */
#define MDI_READY 0x10000000  /* Ready */
#define MDI_IE          0x20000000  /* Interrupt Enable */

#define MDI_PHY_ADDR    1     /* which address on the MDI does our */
                        /* PHY listen to */

#define MDIREG(x) ((x & MDI_REGAD) >> MDI_REGAD_SHIFT)
#define MDIPHY(x) ((x & MDI_PHYAD) >> MDI_PHYAD_SHIFT)
#define MDICMD(x) ((x & MDI_OPCODE) >> MDI_OPCODE_SHIFT)

/* The stats area: Array-Index-Defines. */
#define STA_TRANSGOOD          0    /* properly transmitted frames */
#define STA_TRANSEXCESSCOLL    1    /* frames not transmitted due to */
                              /* excessive collissions */
#define STA_TRANSLATECOLL      2    /* frames not transmitted due to late collissions */
#define STA_TRANSUNDERRUN      3    /* transmit underrun errors */
#define STA_TRANSCARRIERLOST   4    /* carrier sense lost errors */
#define STA_TRANSDEFERRED      5    /* deferred transmits due to activity on the link */
#define STA_TRANSSINGCOLL      6    /* single collissions */
#define STA_TRANSMULTCOLL      7    /* multiple collissions */
#define STA_TRANSTOTALCOLL     8    /* total number of collissions */
#define STA_RECVGOOD           9    /* properly received frames */
#define STA_RECVCRCERRS       10    /* receive CRC errors */
#define STA_RECVALIGNERR      11    /* receive alignment errors */
#define STA_RECVRESERR        12    /* receive resource errors */
#define STA_RECVOVERRUN       13    /* receive overruns */
#define STA_RECVCOLLDET       14    /* receive collission detect errors */
#define STA_RECVSHORTFRAME    15    /* receive short frames */
#define STA_SIGNATURE         16    /* signature dword (0xA005 or 0xA007) */

/* Structure of a Command Block Header */
/* General Structure of a Command Block Header (or, mostly identical, a
 * Receive Frame Header):
 * Bytes 0-3: Command, and status bits
 * Bytes 4-7: Pointer to next CB
 * Byte  8+:  Additional data for specific commands */
/* these are set by the driver */
#define CB_EL           0x80000000  /* End of List (this is the last element) */
#define CB_S            0x40000000  /* Suspend after this */
#define CB_I            0x20000000  /* Generate Interrupt after this */
#define CB_NC           0x00100000  /* Don't automatically insert SrcAddr */
#define CB_SF           0x00080000  /* Device operating in flexible mode */
/* and these are modified by the NIC */
#define CB_C            0x00008000  /* DMA has completed */
#define CB_OK           0x00002000  /* Command was executed w/o error */
#define CB_U            0x00001000  /* Underrun(s) occured */

/* Receive Frame Header - bitmasks are mostly the same as in the command
 * blocks, so this is mostly just a list of aliases */
#define RF_EL     CB_EL
#define RF_S      CB_S
#define RF_SF     CB_SF
#define RF_C      CB_C
#define RF_OK     CB_OK
#define RF_NORES  0x00000100  /* Ran out of Buffer space */
#define RF_TYPELEN      0x00000020  /* Received Frame was Type Frame */

/* defines for the Command Block */
#define CB_CMDMASK      0x00070000  /* Mask for the Command */
#define CB_CMDSHIFT     16          /* and how far we have to shift it */
#define CBCMD(x)  ((x & CB_CMDMASK) >> CB_CMDSHIFT)

/* TBD Transport Buffer Descriptor */
#define TBD_NUMBERMASK  0xFF000000  /* Mask for the TBD number */
#define TBD_NUMBERMASK_SHIFT  24
#define TBD_TRATHRMASK  0x00FF0000  /* Transmit Threshold */
#define TBD_TRATHRMASK_SHIFT  16
#define TBD_BYTECNTMASK 0x00003FFF  /* Transmit Control Block Byte Count */

#define TBDNUM(x) ((x & TBD_NUMBERMASK) >> TBD_NUMBERMASK_SHIFT)

/* RFD Receive Frame Descriptor */
#define RFD_SIZEMASK    0x3FFF0000  /* Size of the Buffer */
#define RFD_SIZEMASK_SHIFT    16
#define RFD_COUNTMASK   0x00003FFF  /* used bytes in the buffer */
#define RFD_EOF         0x00008000  /* set by nic when buffer is filled */
#define RFD_F           0x00004000  /* set by nic when count is updated */

#define RFD_GETSIZE(x)  ((x & RFD_SIZEMASK) >> RFD_SIZEMASK_SHIFT)

/* Defines concerning the Configuration Bytes */
#define NUMCONFBYTES          22      /* We have 22 Configuration Bytes */
/* Byte 0 */
#define CONFBYTECOUNTMASK     0x3F  /* Mask for number of configuration bytes */
/* Byte 6 */
#define COBY_SAVEBADFRAMES    0x80  /* Save Bad Frames */
#define COBY_DISCARDOVERRUN   0x40  /* do not Discard Overrun Frames */
#define COBY_CIINTERRUPT      0x08  /* don't generate interrupt if CU goes */
                              /* to suspend, only when it goes to idle */
/* Byte 10 */
#define COBY_LOOPBACKMODE     0xC0  /* Loopback enable / mode */
#define COBY_NOSOURCEADINS    0x08  /* No source address insertion */
/* Byte 15 */
#define COBY_BROADDISABLE     0x02  /* Broadcast Disable */
#define COBY_PROMISC          0x01  /* Promiscuous Mode */
/* Byte 18 */
#define COBY_RECEIVECRC       0x04  /* CRC gets transferred to memory */
#define COBY_TRANSMITPADDING  0x02  /* Pad short frames to 60 Bytes */
#define COBY_STRIPPING        0x01  /* Strip everything that exceeds the */
                              /* length field of a incoming packet */
/* Byte 21 */
#define COBY_MULTICASTALL     0x08  /* Listen to ANY multicast address */

#define MAXPACKETSIZE 1518    /* Developer manual states 2600, but that */
                        /* is impossible over ethernet. Because we */
                        /* simulate a card attached to standard 100 */
                        /* MBit Ethernet, this value is more sane. */

#define SIMFILENAMELEN  512   /* Space for filename of eeprom / flashrom */
                        /* files */

/* General note (for reference): format of a ethernet packet.
 * -------------------------------------------------------------------
 * | Destination | Source    | Length/Type | Data          | CRC     |
 * -------------------------------------------------------------------
 *   Byte 0-5      Byte 6-11   Byte 12-13    46-1500 Bytes   4 Bytes
 * This brings us to a total maximum frame length of
 * 1500+6+6+2+4 = 1518 Bytes. Minimum Length is 64 Bytes.
 * If the actual data is less than 46 bytes it has to be padded.
 * The eepro100 offers a feature to automatically do that. The length
 * field can be used to mark the number of actually used bytes in the
 * data field. However, it also has a second meaning: if length is
 * greater than 1500 (0x05DC), then it is not a length but instead a
 * type, marking higher level protocols. Some often seen types:
 *       0x0800  IPv(4|6)                  0x0806  ARP
 * Multi-Byte-Values (like length/type) are stored Big-Endian (network
 * byte order).
 * Note that the CRC field is not / not usually stored in memory when the
 * NIC transmits / receives a packet. It can be explicitly requested
 * on reception, but not on transmission (so you cannot send
 * artificially corrupted packets). The sig_eth layer we use for sending/
 * receiving also expects the packets without CRC.
 */

struct css {
      unsigned int nr;

      struct sig_boolean *sig_power;
      unsigned int state_power;
      struct sig_boolean *sig_n_reset;
      struct sig_pci_bus_main *pci_bus;
      struct sig_boolean_or *pci_int;
      struct sig_eth *sig_eth;
      struct sig_boolean *sig_busy;

      uint16_t eeprom_data[SZ_E100EEPROM]; /* EEPROM size of 82557:
                                    * 64 x 16 bit. */
      unsigned char eeprom_selected; /* whether the EEPROM is "selected"
                              * (if not it will not react) */
      unsigned char eeprom_clock;    /* status of the EEPROM clock signal */
      unsigned char eeprom_opcode;   /* Which opcode was requested */
      unsigned char eeprom_opcbits;  /* How many bits of the opcode have
                              * been written already */
      unsigned char eeprom_address;  /* What address shall be read /
                              * written / erased */
      unsigned char eeprom_adbits;   /* How many bits of the address have
                              * been written already */
      unsigned char eeprom_datapos;  /* Which bit of the eeprom-register
                              * will be read on next access */
      unsigned char eeprom_ewenable; /* Erase / write enabled */
      char eeprom_filename[SIMFILENAMELEN];
      
      unsigned char flashrom[SZ_E100FLASHROM];/* The flashrom of the card */

      uint32_t config_space[64];     /* PCI Configuration space (64 ulongs 
                              * == 256 Bytes) */
      uint32_t CUBase;         /* Offset added to all Pointers given
                              * to the CU */
      uint32_t RUBase;         /* Offset added to all Pointers given
                              * to the RU */
      uint32_t CUPos;           /* Current pointer for the command unit */
      uint32_t RUPos;           /* Current pointer for the receive unit */
      uint32_t StatDumpAddress; /* Where we shall dump our stats */
      uint32_t statcounters[17];/* Statistic counters */
            
      uint16_t mdiregs[32];   /* Management Data Interface Registers */
      
      unsigned char cardregs[128];  /* The registers of the card */
      
      unsigned char mac[6];         /* The MAC Address of the device */
      
      unsigned char confbytes[NUMCONFBYTES];    /* Configuration Bytes. */
      
      unsigned char mcasthash[8];   /* Multicast Hash */
      
      unsigned char tmprpacket[MAXPACKETSIZE]; /* temporary space for
                                      * receiving packet */
      unsigned char tmpspacket[MAXPACKETSIZE]; /* temporary space for
                                      * sending packet */

      unsigned int recv_packet_loss;
      unsigned int send_packet_loss;

      int active; /* For the network LED in the GUI */
};

#define LONGCARDREG(css,reg)  ((uint32_t *)&css->cardregs[reg])
#define SHORTCARDREG(css,reg) ((uint16_t *)&css->cardregs[reg])
#define CHARCARDREG(css,reg)  ((uint8_t *)&css->cardregs[reg])
#define E100MEMADDR(css)      ((uint32_t)(css->config_space[PCI_BASE_ADDRESS_0>>2] & PCI_BASE_ADDRESS_MEM_MASK))
#define E100IOADDR(css)       ((uint16_t)((css->config_space[PCI_BASE_ADDRESS_1>>2] & PCI_BASE_ADDRESS_IO_MASK)&0x0000ffff))
/* FLASHADDR is the address in PCI_BASE_ADDRESS_2, ROMADDR is PCI_ROM_ADDRESS */
#define E100FLASHADDR(css)    ((uint32_t)(css->config_space[PCI_BASE_ADDRESS_2>>2] & PCI_BASE_ADDRESS_MEM_MASK))
#define E100ROMADDR(css)      ((uint32_t)(css->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_MASK))

#if (DEBUGMASK > 0)

#define TRACE(lvl, fmt, arg...) \
if ((lvl & DEBUGMASK)) { \
      char tmplog[2]; \
      tmplog[0] = '0' + nr; \
      tmplog[1] = 0; \
      faum_log(FAUM_LOG_DEBUG, COMP, tmplog, "[%20s/%4d] " fmt , \
            __FUNCTION__, __LINE__, arg); \
}

static char PORTOpcodes[8][50] = { "Software Reset", "Self-Test",
                           "Selective Reset", "Dump",
                           "unsupported on this model! (Dump Wake-Up)",
                           "undefined", "undefined", "undefined" };
static char EEPROMOpcodes[4][20] = { "Special", "Write Reg.",
                             "Read Reg.", "Erase Reg." };
static char CUCommands[16][50] = { "NOOP", "CU Start", "CU Resume",
                           "undefined", "Load Dump Counters Address",
                           "Dump Stat Counters", "Load CU Base",
                           "Dump and Reset Stat Counters", "undefined",
                           "unsupported on this model! (CU Static Resume)",
                           "undefined", "undefined", "undefined",
                           "undefined", "undefined", "undefined" };
static char RUCommands[8][50] = { "NOOP", "RU Start", "RU Resume",
                          "unsupported on this model! (Receive DMA Redirect)",
                          "RU Abort", "Load Header Data Size",
                          "Load RU Base", "undefined" };
static char MDICommands[4][30] = { "undefined", "Write", "Read", "Undefined" };
static char CBCommands[8][30] = { "NOOP", "Individual Address Setup", "Configure",
                          "Multicast Address Setup", "Transmit",
                          "Load Microcode", "Dump", "Diagnose" };

#else /* DEBUGGING */

#define TRACE(lvl, fmt, arg...) while(0) { } /* ; */

#endif /* DEBUGGING */

#if (DEBUGMASK & DEBUG_DUMPACKETS)
static void
chip_intel_82557_dumpdata(int len, unsigned char * packet)
{
      int i;
      fprintf(stderr,"    0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F ");
      for (i = 0; i < len; i++) {
            if ((i % 0x10) == 0) {
                  fprintf(stderr,"\n%02x ",(i/0x10));
            }
            fprintf(stderr,"%02x ",packet[i]);
      }
      fprintf(stderr,"\n");
}
#else
#define chip_intel_82557_dumpdata(len, pack) if (0) { }
#endif

#if WARNINGS
#define WARN(fmt, arg...) \
if (1) { \
      char tmplog[2]; \
      tmplog[0] = '0' + nr; \
      tmplog[1] = 0; \
      faum_log(FAUM_LOG_WARNING, COMP, tmplog, "in %s: " fmt , \
                  __FUNCTION__, arg); \
}
#else /* WARNINGS */
#define WARN(fmt, arg...) if (0) { } /* ; */
#endif  /* WARNINGS */

static void
chip_intel_82557_read(
      struct css *css,
      uint32_t addr,
      void *_to,
      unsigned int len
)
{
      uint8_t *to = (uint8_t *) _to;

      while (0 < len) {
            unsigned int count;
            unsigned int bs;
            uint32_t val;

            count = len;
            if (4 < (addr & 3) + count) {
                  count = 4 - (addr & 3);
            }
            bs = ((1 << count) - 1) << (addr & 3);

            if (sig_pci_bus_mr(css->pci_bus, css, addr & ~3, bs, &val) != 0) {
                  sig_pci_bus_main_type_addr(css->pci_bus, css,
                              SIG_PCI_BUS_MR, addr & ~3);
                  /* delay... */
                  sig_pci_bus_main_read_data(css->pci_bus, css,
                              bs, &val);
            }

            memcpy(to, (uint8_t *) &val + (addr & 3), count);

            addr += count;
            len -= count;
            to += count;
      }
}

static void
chip_intel_82557_write(
      struct css *css,
      uint32_t addr,
      const void *_from,
      unsigned int len
)
{
      const uint8_t *from = (const uint8_t *) _from;

      while (0 < len) {
            unsigned int count;
            unsigned int bs;
            uint32_t val;

            count = len;
            if (4 < (addr & 3) + count) {
                  count = 4 - (addr & 3);
            }
            bs = ((1 << count) - 1) << (addr & 3);

            memcpy((uint8_t *) &val + (addr & 3), from, count);

            if (sig_pci_bus_mw(css->pci_bus, css, addr & ~3, bs, val) != 0) {
                  sig_pci_bus_main_type_addr(css->pci_bus, css,
                              SIG_PCI_BUS_MW, addr & ~3);
                  /* delay... */
                  sig_pci_bus_main_write_data(css->pci_bus, css,
                              bs, val);
            }

            addr += count;
            len -= count;
            from += count;
      }
}

static void
chip_intel_82557_irq_update(struct css *css)
{
      if ((css->cardregs[3] & SCB_ICB_M) == 0
       && css->cardregs[1] != 0x00) {
            sig_boolean_or_set(css->pci_int, css, 1);
      } else {
            sig_boolean_or_set(css->pci_int, css, 0);
      }
}

static void
chip_intel_82557_reseteepromstatus(struct css *css)
{
      css->eeprom_opcode = 0;
      css->eeprom_opcbits = 0;
      css->eeprom_address = 0;
      css->eeprom_adbits = 0;
      css->eeprom_datapos = 0;
}

/* The following function resets the cards MDI registers to their poweron
 * state. It tries to fill them with realistic values, i.e. values a real
 * card would have.
 * FIXME fox: fill all, not just selected
 */
static void
chip_intel_82557_resetmdiregs(struct css *css)
{
      css->mdiregs[ 0] = 0x3100;    /* Status Register 0 */
      css->mdiregs[ 1] = 0x782d;    /* Status Register 1 */
      css->mdiregs[ 2] = 0x02A8;    /* MDI Identification Register 2 */
      css->mdiregs[ 3] = 0x0154;    /* MDI Identification Register 3 */
      css->mdiregs[ 4] = 0x41e1;    /* Auto Negotiation Advertisement */
                              /* Register 4 */
      css->mdiregs[ 5] = 0x41e1;    /* Auto Negotiation Link Partner */
                              /* Ability Register 5 */
      css->mdiregs[ 6] = 0x0003;    /* Auto Negotiation Expansion */
                              /* Register 6 */
      css->mdiregs[16] = 0x0003;    /* Status and Control Register 16 */
      css->mdiregs[18] = 0x0000 | MDI_PHY_ADDR; /* Clock Synthesis */
                              /* and Control Register 18 */
}

/* The following function resets the cards conf bytes to
 * sane values.
 * FIXME fox: fill all, not just selected
 */
static void
chip_intel_82557_resetconfbytes(struct css *css)
{
      css->confbytes[ 0] = NUMCONFBYTES;  /* Byte Count */
      css->confbytes[ 6] = 0x32; /* Save bad frames, discard overrun recv, CI int and others */
      css->confbytes[15] = 0xC8; /* Broadcast Disable, Promisc. Mode */
      css->confbytes[18] = 0x02; /* padding, stripping, receive crc */
      css->confbytes[21] = 0x05; /* Multicast All */
}

/* Checks if the E100 is in any loopback mode. Returns 1 if it is. */
static int
chip_intel_82557_checkloopbackmode(struct css *css)
{
      if ((css->confbytes[10] & COBY_LOOPBACKMODE)) {
            return 1;
      }
      if ((css->mdiregs[0] & 0x4000)) {
            return 1;
      }
      return 0;
}

/* return the 6-bit index into the multicast
 * table. Taken from umne2000.c, which took it from Bochs mcast_index()
 */
static unsigned char
chip_intel_82557_mcasthash(unsigned char *dst)
{
#define POLYNOMIAL 0x04c11db6
      unsigned long crc = 0xffffffffL;
      int carry, i, j;
      unsigned char b;

      for (i = 6; --i >= 0;) {
            b = *dst++;
            for (j = 8; --j >= 0;) {
                  carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
                  crc <<= 1;
                  b >>= 1;
                  if (carry) {
                        crc = ((crc ^ POLYNOMIAL) | carry);
                  }
            }
      }
      return (crc >> 25) & 0x3F; /* Selects Bits 2 to 7 */
#undef POLYNOMIAL
}

/*
 * Checks if a packet is addressed for us.
 * This can be the case if it is
 * a) a broadcast
 * b) unicast for our mac
 * c) multicast for a multicast address we listen to
 */
static int
chip_intel_82557_ispacketforme(struct css *css, unsigned char *tpacket)
{
      if (memcmp(tpacket, css->mac, 6) == 0) {
            return 1; /* This is for our unicast mac-address */
      }
      if (css->confbytes[15] & COBY_PROMISC) {
            /* We are in promiscuous mode, so everything is for us */
            return 1;
      }
      if (tpacket[0] == 0xFF
       && tpacket[1] == 0xFF
       && tpacket[2] == 0xFF
       && tpacket[3] == 0xFF
       && tpacket[4] == 0xFF
       && tpacket[5] == 0xFF) {
            /* A broadcast always is for us too */
            if (css->confbytes[15] & COBY_BROADDISABLE) {
                  /* ...Unless we are set to ignore them */
                  TRACE(DEBUG_OTHER, "%s\n",
                        "Broadcast received, but Broadcast "
                        "reception is disabled!");
                  return 0;
            } else {
                  return 1;
            }
      }
      if (tpacket[0] & 0x01) { /* Multicast Bit is set */
            if (css->confbytes[21] & COBY_MULTICASTALL) {
                  /* We accept any multicast address */
                  return 1;
            } else {
                  /* The E100 uses a hash table to check if the packet
                   * COULD be for any of its multicast addresses.
                   * Of course, that means it will receive some packets
                   * that weren't meant for it.
                   * The hash function it uses is not documented, so we
                   * just use the same as the NE2000. */
                  /* FIXME fox: check / Handle multicast correctly! */
                  unsigned int idx;

                  idx = chip_intel_82557_mcasthash(&tpacket[0]);
                  assert(/* 0 <= (idx >> 3) && */ (idx >> 3) <= 7);
                  return (css->mcasthash[idx >> 3] & (1 << (idx & 0x7)));
            }
      }
      return 0;
}

/*
 * This function handles packets received from the network.
 * It feeds them to the driver, writes headers, generates ints etc.
 */
static void
chip_intel_82557_handlereceivedpacket(struct css *css, int len)
{
      struct {
            uint32_t val0;
            uint32_t val4;
            uint32_t val8;
            uint32_t valc;
      } rfd;
      unsigned char * tmppacket;
      
      tmppacket = &css->tmprpacket[0];
      if (len < 6) {
            /* No valid packet */
            return;
      }

      /*
       * We did receive a packet (though we do not yet know whether we will
       * promote it, we physically received it), so switch led on.
       */
      css->active = 1;

      chip_intel_82557_dumpdata(len, tmppacket);

      if (css->RUBase == 0
       && css->RUPos == 0) {
            /* no receive area */
            css->statcounters[STA_RECVRESERR]++;
            return;
      }
      if (RUSTATUS(css->cardregs[0]) != RUS_READY) {
            /* We are not ready to receive. Abort. */
            if (RUSTATUS(css->cardregs[0]) == RUS_NORES) {
                  /* Count the overrun */
                  TRACE(DEBUG_OTHER, "%s\n",
                        "RU overrun, Packet dropped.");
                  css->statcounters[STA_RECVOVERRUN]++;
            }
            return;
      }

      if (chip_intel_82557_ispacketforme(css, tmppacket) <= 0) {
            TRACE(DEBUG_OTHER, "%s\n","... but packet is not for me.");
            return;
      }

      /* Time to read the Receive Frame Descriptor */
      chip_intel_82557_read(css, css->RUBase + css->RUPos, &rfd, sizeof(rfd));

      if ((rfd.valc & RFD_EOF) == RFD_EOF
       || (rfd.valc & RFD_F) == RFD_F) {
            WARN("Pre-Used RFD detected @%08x\n", css->RUPos);
            css->statcounters[STA_RECVRESERR]++;
            css->cardregs[0] &= ~SCB_STATUS_RUS;
            css->cardregs[0] |= RUS_NORES << SCB_STATUS_RUS_SHIFT;
            css->cardregs[1] |= SCB_STATACK_RNR;
      }

      TRACE(DEBUG_OTHER, "Packet received (%d bytes)\n", len);

      if (css->confbytes[18] & COBY_STRIPPING) {
            int leninpacket;

            leninpacket = be16_to_cpu(*(unsigned short *) &tmppacket[12]);
            leninpacket += 14;
            if (leninpacket < len && leninpacket <= 1514) {
                  TRACE(DEBUG_OTHER,
                        "... stripped to length in packet: %d\n",
                        leninpacket);
                  len = leninpacket;
            }
      }
      if (0   == be16_to_cpu(*(unsigned short *)&tmppacket[12])
       || 1518 < be16_to_cpu(*(unsigned short *)&tmppacket[12])) {
            rfd.val0 |= RF_TYPELEN;
      } 
      if (css->confbytes[18] & COBY_RECEIVECRC) {
            /* FIXME fox: calculate CRC and put it into the packet. */
      }
      if (rfd.val0 & RF_SF) {
            /*
             * Flexible Mode
             */
            uint32_t rbdstart;
            int lenremain;
            
            lenremain = len;
            WARN("RFD @%08x is not in simplified mode!\n", css->RUPos);
            rbdstart = rfd.val8;

            while (0 < lenremain) {
                  uint32_t bufpnt;
                  uint32_t bufsiz;
                  uint32_t bufstat;
                  
                  chip_intel_82557_read(css,
                              css->RUBase + rbdstart + 0x00,
                              &bufstat, 4);
                  chip_intel_82557_read(css,
                              css->RUBase + rbdstart + 0x08,
                              &bufpnt, 4);
                  chip_intel_82557_read(css,
                              css->RUBase + rbdstart + 0x0C,
                              &bufsiz, 4);
                  if (bufpnt == 0 || bufpnt == 0xffffffffUL) {
                        /* Nonsense values, don't destroy data */
                        TRACE(DEBUG_OTHER,
                              "Nonsense-Pointer (%08x) for "
                              "Receive Block!\n",
                              bufpnt);
                        break;
                  }
                  if (bufsiz == 0 || 0x0000ffffUL < bufsiz) {
                        /* Nonsense blocksize */
                        TRACE(DEBUG_OTHER,
                              "Nonsense Receive Block Size: %08x\n",
                              bufsiz);
                        break;
                  }
                  TRACE(DEBUG_OTHER,
                        "%d Bytes Receive Block, %d Bytes remaining, "
                        "bufstat: %08x\n",
                        (int) bufsiz, lenremain, bufstat);
                  if (bufsiz < lenremain) {
                        chip_intel_82557_write(css,
                                    css->RUBase + bufpnt,
                                    tmppacket, bufsiz);
                        tmppacket += bufsiz;
                        lenremain -= bufsiz;
                  } else {
                        chip_intel_82557_write(css,
                                    css->RUBase + bufpnt,
                                    tmppacket, lenremain);
                        tmppacket += lenremain;
                        lenremain = 0;
                  }
                  /* Now we need to update bufstat - but I have no idea
                   * HOW */
                  /* bufstat |= 0x10101010; */
                  chip_intel_82557_write(css,
                              css->RUBase + rbdstart + 0x00,
                              &bufstat, 4);
                  chip_intel_82557_write(css,
                              css->RUBase + rbdstart + 0x0C,
                              &bufsiz, 4);
                  chip_intel_82557_read(css,
                              css->RUBase + rbdstart + 0x04,
                              &rbdstart, 4);
            }
            if (lenremain == 0) {
                  rfd.val0 |= RF_OK;  /* Reception OK */
                  rfd.valc &= RFD_COUNTMASK;  /* write length to header */
                  rfd.valc |= len;
                  rfd.valc |= RFD_EOF | RFD_F;
                  css->statcounters[STA_RECVGOOD]++;
            } else {
                  rfd.val0 |= RF_NORES;
                  css->statcounters[STA_RECVRESERR]++;
            }

      } else {
            /*
             * Simplified Mode
             */
            /* This is not mentioned in the documentation, but win
             * seems to use it: The special size "0" seems to mean
             * "unlimited". */
            if (RFD_GETSIZE(rfd.valc) < len
             && RFD_GETSIZE(rfd.valc) != 0) {
                  /* That frame is too large for this RF */
                  WARN("Dropping %d byte frame, too large for RFD "
                        "with %ld bytes space!\n",
                        len, RFD_GETSIZE(tmps));
                  rfd.val0 |= RF_NORES;
                  css->statcounters[STA_RECVRESERR]++;
                  css->cardregs[0] &= ~SCB_STATUS_RUS;
                  css->cardregs[0] |= RUS_NORES << SCB_STATUS_RUS_SHIFT;
                  css->cardregs[1] |= SCB_STATACK_RNR;
            } else {
                  chip_intel_82557_write(css,
                              css->RUBase + css->RUPos + 0x10,
                              &tmppacket[0], len);
                  rfd.val0 |= RF_OK;  /* Reception OK */
                  rfd.valc &= RFD_COUNTMASK;  /* write length to header */
                  rfd.valc |= len;
                  rfd.valc |= RFD_EOF | RFD_F;
                  css->statcounters[STA_RECVGOOD]++;
            }
      }

      rfd.val0 |= RF_C; /* Reception complete (successful or not) */

      /* Write back headers (containing the status) */
      chip_intel_82557_write(css, css->RUBase + css->RUPos, &rfd, sizeof(rfd));
      if ((rfd.val0 & RF_EL) == RF_EL) {
            /* End of List, no more memory */
            WARN("%s\n", "Receive Buffers full, next packet "
                  "will be lost");
            css->cardregs[1] |= SCB_STATACK_RNR;
            css->cardregs[0] &= ~SCB_STATUS_RUS;
            css->cardregs[0] |= RUS_NORES << SCB_STATUS_RUS_SHIFT;
      } else if ((rfd.val0 & RF_S) == RF_S) {
            /* Suspend after this */
            css->cardregs[1] |= SCB_STATACK_RNR;
            css->cardregs[0] &= ~SCB_STATUS_RUS;
            css->cardregs[0] |= RUS_SUSPENDED << SCB_STATUS_RUS_SHIFT;
      }

      /* Read next chain entry - this happens even at the End of List! */
      css->RUPos = rfd.val4;

      css->cardregs[1] |= SCB_STATACK_FR;
      chip_intel_82557_irq_update(css);
}

/*
 * This parses a packet as given to the TRANSMIT Command-Unit Command,
 * and then sends it out to the network.
 * Returns 1 on success, 0 on error.
 */
static int
chip_intel_82557_parseandsendpacket(
      struct css *css,
      unsigned long memaddr,
      uint32_t CBHeader
)
{
      int len;
      int pos;
      uint32_t tmps[2];
      int i;
      uint32_t tmptbd[2];
      unsigned char * tmppacket;
      
      len = 0;
      pos = 0;
      if (chip_intel_82557_checkloopbackmode(css)) {
            /* Loopbackmode, so put it into the RECEIVE buffer */
            tmppacket = css->tmprpacket;
      } else {
            tmppacket = css->tmpspacket;
      }
      chip_intel_82557_read(css,
                  css->CUBase + memaddr + 8, &tmps[0], 8);
      if ((CBHeader & CB_SF) == 0 && tmps[0] == 0xFFFFFFFF) {
            /* simplified mode */
            if ((tmps[1] & TBD_BYTECNTMASK) == 0) {
                  WARN("%s\n", "Transmit for 0 Byte packet in simplified mode!");
                  return 0; /* 0 Bytes to send?! */
            }
            len = tmps[1] & TBD_BYTECNTMASK;
            if (MAXPACKETSIZE < len) {
                  WARN("Packet too large to send! (%d > %d)\n",
                        len, MAXPACKETSIZE);
                  return 0;
            }
            chip_intel_82557_read(css,
                        css->CUBase + memaddr + 0x10,
                        &tmppacket[0], (tmps[1]&TBD_BYTECNTMASK));
      } else {
            /* flexible mode */
            TRACE(DEBUG_OTHER,
                  "`%d TBDs, starting @0x%08x, %d Bytes in Header\n",
                  TBDNUM(tmps[1]), tmps[0], tmps[1] & TBD_BYTECNTMASK);
            if (0 < (tmps[1] & TBD_BYTECNTMASK)) {
                  len += tmps[1] & TBD_BYTECNTMASK;
                  if (MAXPACKETSIZE < len) {
                        WARN("Packet too large to send! (%d > %d)\n",
                              len, MAXPACKETSIZE);
                        return 0;
                  }
                  chip_intel_82557_read(css,
                        css->CUBase + memaddr + 12,
                        &tmppacket[0], tmps[1] & TBD_BYTECNTMASK);
                  pos += tmps[1] & TBD_BYTECNTMASK;
            }
            for (i = 0; i < TBDNUM(tmps[1]); i++) {
                  chip_intel_82557_read(css,
                        css->CUBase + tmps[0] + i * 8, &tmptbd[0], 8);
                  len += tmptbd[1] & TBD_BYTECNTMASK;
                  if (MAXPACKETSIZE < len) {
                        WARN("Packet too large to send! (%d > %d)\n",
                              len, MAXPACKETSIZE);
                        return 0;
                  }
                  chip_intel_82557_read(css,
                              tmptbd[0], &tmppacket[pos],
                                  (tmptbd[1] & TBD_BYTECNTMASK));
                  pos += tmptbd[1] & TBD_BYTECNTMASK;
            }
            TRACE(DEBUG_OTHER, "`%d bytes total.\n", len);
            if (len == 0) {
                  WARN("%s\n", "Transmit for 0 Byte packet in flexible mode!");
                  return 0; /* 0 Bytes to send?! */
            }
      }
      if ((CBHeader & CB_NC) == 0) { /* Fill in SRC if requested */
            memcpy(&tmppacket[6], &css->mac[0], 6);
      }
      if ((css->confbytes[18] & COBY_TRANSMITPADDING)) {
            for (i = len; i < 60; i++) {
                  tmppacket[i] = 0x7F;
                  len++;
            }
      }

      /* switch led on */
      css->active = 1;

      /* this would be the place to fill in crc... however, this is not
         needed, as sig_eth expects the packet without crc. */
      if (chip_intel_82557_checkloopbackmode(css)) {
            chip_intel_82557_handlereceivedpacket(css, len);
      } else {
            sig_eth_send(css->sig_eth, css, tmppacket, len);
      }

      return 1;
}

/*
 * The following function takes the next Command Block, pointed to
 * by CUBase+CUPos, and handles it. It then updates the result bits in
 * the CB, and sets CUPos to the next position in the chained Command
 * Block List.
 * It returns 0 when it reaches the end of the Command Block List,
 * or 1 if there are more commands to handle.
 */
static int
chip_intel_82557_handlenextcb(struct css *css)
{
      uint32_t CBHeader;
      uint32_t tmps[2];
      int res;
      unsigned int idx;
      uint16_t mccount;
      uint16_t mcoffs;
      
      if (css->CUPos == 0 && css->CUBase == 0) {
            return 0;
      }
      chip_intel_82557_read(css,
                  css->CUBase + css->CUPos, &CBHeader, 4);
      TRACE(DEBUG_OTHER,
            "Handling Command Block: Command = %d (%s) I:%d EL:%d S:%d\n",
            CBCMD(CBHeader), CBCommands[CBCMD(CBHeader)],
            ((CBHeader & CB_I) == 0) ? 0 : 1,
            ((CBHeader & CB_EL) == 0) ? 0 : 1,
            ((CBHeader & CB_S) == 0) ? 0 : 1);
      /* The default is "all OK" - individual handlers may change that
         if they encounter an error */
      CBHeader |= CB_C | CB_OK;
      switch (CBCMD(CBHeader)) {
      case 0: /* NOOP */
            break;
      case 1: /* Individual Address Setup */
            chip_intel_82557_read(css,
                  css->CUBase + css->CUPos + 8, &tmps[0], 8);
            memcpy(&css->mac[0], &tmps[0], 6);
            TRACE(DEBUG_OTHER,
                  "Address set to %02x:%02x:%02x:%02x:%02x:%02x\n",
                  css->mac[0], css->mac[1], css->mac[2],
                  css->mac[3], css->mac[4], css->mac[5]);
            break;
      case 2: /* Configure */
            chip_intel_82557_read(css,
                  css->CUBase + css->CUPos + 8, &tmps[0], 4);
            tmps[0] &= CONFBYTECOUNTMASK;
            if (tmps[0] > NUMCONFBYTES) {
                  tmps[0] = NUMCONFBYTES;
            }
            TRACE(DEBUG_OTHER, "loading %d confbytes\n", tmps[0]);
            chip_intel_82557_read(css,
                  css->CUBase + css->CUPos + 10,
                  &css->confbytes[1], tmps[0] - 1);
            break;
      case 3: /* Multicast Address Setup */
            /* First reset the hash list, then read new one supplied by
             * the driver. */
            memset(&css->mcasthash[0], 0x00, 8);
            chip_intel_82557_read(css,
                  css->CUBase + css->CUPos + 8, &mccount, 2);
            mccount &= 0x3FFF; /* Only 14 Bits are valid */
            mcoffs = 10; /* no, not 0x10! */
            while (6 <= mccount) {
                  chip_intel_82557_read(css,
                        css->CUBase + css->CUPos + mcoffs, &tmps[0], 6);
                  idx = chip_intel_82557_mcasthash((char *)&tmps[0]);
                  assert(/* 0 <= (idx >> 3) && */ (idx >> 3) <= 7);
                  css->mcasthash[idx >> 3] |= (1 << (idx & 0x7));
                  mccount -= 6;
                  mcoffs += 6;
            }
            break;
      case 4: /* Transmit */
            CBHeader &= ~CB_U; /* No Underrun */
            TRACE(DEBUG_OTHER, "`Insert Source Address: %s; %s mode.\n",
                  ((CBHeader & CB_NC)==0) ? "Yes" : "No ",
                  ((CBHeader & CB_SF)==0) ? "simplified" : "flexible");
            chip_intel_82557_parseandsendpacket(css, css->CUPos, CBHeader);
            css->statcounters[STA_TRANSGOOD]++;
            break;
      case 5: /* Load Microcode */
            /* I'm a simulator damnit, I have no changeable microcode */
            WARN("%s%s\n","Driver is trying to update my Microcode! -> ",
                  "I'll ignore it and continue, that should work.");
            break;
      case 6: /* Dump */
            WARN("%s\n","Driver is trying to dump my internal status!");
            break;
      case 7: /* Diagnose */
            /* Note: This already is the implementation. There is just
             *       nothing to do since we have no physical hardware
             *       to check. */
            break;
      };
      /* Write back header (containing the status) */
      chip_intel_82557_write(css,
            css->CUBase + css->CUPos, &CBHeader, 4);
      res = 1;
      if ((CBHeader & CB_EL) == CB_EL) {
            css->cardregs[1] |= SCB_STATACK_CNA;
            css->cardregs[0] &= ~SCB_STATUS_CUS;
            css->cardregs[0] |= CUS_IDLE << SCB_STATUS_CUS_SHIFT;
            res = 0;
      } else if ((CBHeader & CB_S) == CB_S) {
            css->cardregs[1] |= SCB_STATACK_CNA;
            css->cardregs[0] &= ~SCB_STATUS_CUS;
            css->cardregs[0] |= CUS_SUSPENDED << SCB_STATUS_CUS_SHIFT;
            res = 0;
      }
      /* Read next chain entry - this happens even at the End of List! */
      chip_intel_82557_read(css,
            css->CUBase + css->CUPos + 4, &css->CUPos, 4);
      if ((CBHeader & CB_I) == CB_I) {
            css->cardregs[1] |= SCB_STATACK_CX;
      }
      chip_intel_82557_irq_update(css);
      return res;
}

static void
chip_intel_82557_handleeepromclock(struct css *css, unsigned char disig)
{
      if (css->eeprom_opcbits < EEPROM_OPCODEBITS) {
            if (css->eeprom_opcbits != 0
             || disig) {
                  /* The First Bit of the opcode always has to be a 1 */
                  css->eeprom_opcode <<= 1;
                  css->eeprom_opcode += disig;
                  css->eeprom_opcbits++;
            }
      } else {  /* Opcode already complete */
            if (css->eeprom_adbits < EEPROM_ADDRESSBITS) {
                  css->eeprom_address <<= 1;
                  css->eeprom_address += disig;
                  css->eeprom_adbits++;
                  if (css->eeprom_adbits == EEPROM_ADDRESSBITS) {
                        /* Just completed the address */
                        css->eeprom_datapos=0;
                        TRACE(DEBUG_EEPROM,
                              "eeprom opcode %1d (%s) addr %02d!\n",
                              css->eeprom_opcode,
                              EEPROMOpcodes[css->eeprom_opcode & 0x03],
                              css->eeprom_address);
                  }
            } else { /* Address already complete too */
                  if (16 <= css->eeprom_datapos) {
                        /* Transfer complete, reset to initial state */
                        chip_intel_82557_reseteepromstatus(css);
                  } else {
                        css->eeprom_datapos++;
                  }
            }
      }
}

static void
chip_intel_82557_eepromread(struct css *css, unsigned char * val)
{
      *val &= ~EEPROM_EEDO;
      if (16 < css->eeprom_datapos)  {
            return;
      }
      if ((css->eeprom_data[css->eeprom_address] >> (16 - css->eeprom_datapos)) & 1) {
            *val |= EEPROM_EEDO;
      }
}

static void
chip_intel_82557_eepromwrite(struct css *css, unsigned char * val)
{
      *val &= ~EEPROM_EEDO;
      if (16 < css->eeprom_datapos)  {
            return;
      }
      if (css->eeprom_datapos == 0) {
            return;
      }
      if ((css->eeprom_data[css->eeprom_address] >> (16 - css->eeprom_datapos)) & 1) {
            *val |= EEPROM_EEDO;
      }
      if (! EEPROM_SKSIGNAL(*val)) {
            return;
      }
      css->eeprom_data[css->eeprom_address]
            &= ~(1 << (16 - css->eeprom_datapos));
      if (*val & EEPROM_EEDI) {
            css->eeprom_data[css->eeprom_address]
                  |= (1 << (16 - css->eeprom_datapos));
      }
      if (css->eeprom_datapos == 16) {
            int fd;
            
            fd = open(css->eeprom_filename,
                        O_WRONLY | O_CREAT, 0666);
            if (fd < 0) {
                  WARN("%s\n","Could not save EEPROM to disc!");
            } else {
                  write(fd, css->eeprom_data, SZ_E100EEPROM * 2);
                  close(fd);
            }
            TRACE(DEBUG_EEPROM, "Wrote EEPROM @%u: Value = %04x\n",
                  css->eeprom_address,
                  css->eeprom_data[css->eeprom_address]);
      }
}

/* Some declarations needed later on */
static void
_chip_intel_82557_outw(struct css *css, uint16_t port, uint16_t val);
static void
_chip_intel_82557_outl(struct css *css, uint16_t port, uint32_t val);

static void
_chip_intel_82557_outb(struct css *css, uint16_t port, uint8_t val)
{
      switch(port) {
      case 0x00: /* Status Byte - read only */
            return;
      case 0x01: /* STAT/ACK Byte */
            /* By writing to this, the driver acknowledges handling
             * of interrupts. The bits to which it writes an "1"
             * need to be cleared. */
            TRACE(DEBUG_INTGEN,
                  "ACK for STAT/ACK: %02x: %02x -> %02x\n",
                  (unsigned char) ~val,
                  css->cardregs[1], css->cardregs[1] & ~val);
            css->cardregs[1] &= ~val;
            chip_intel_82557_irq_update(css);
            return;
      case 0x02: /* Command for CU or RU */
            TRACE(DEBUG_OTHER,
                  "Command: for CU: %x (%s)  for RU: %x (%s)\n",
                  CUCMD(val), CUCommands[CUCMD(val)],
                  RUCMD(val), RUCommands[RUCMD(val)]);
            /* do not write the byte to the register - the register is
             * set to 0 in order to signal acceptance */
            css->cardregs[2] = 0;
            /* ----- CU Commands ----- */
            switch(CUCMD(val)) {
            case 0: /* NOOP */
                  break;
            case 1: /* CU Start */
                  css->CUPos = *LONGCARDREG(css, 0x04);
                  /* Fall through to resume! */
            case 2: /* CU Resume */
                  while (chip_intel_82557_handlenextcb(css)) {
                  }
                  break;
            case 4: /* Load Dump Counters Address */
                  css->StatDumpAddress = *LONGCARDREG(css, 0x04);
                  break;
            /* case 5 is below 6 */
            case 6: /* Load CU Base */
                  css->CUBase=*LONGCARDREG(css, 0x04);
                  TRACE(DEBUG_OTHER, "CUBase set to %08x\n", css->CUBase);
                  break;
            case 5: /* Dump Stat Counters */
            case 7: /* Dump and Reset Stat Counters */
                  css->statcounters[STA_SIGNATURE] = 0xA000 + CUCMD(val);
                  chip_intel_82557_write(css,
                              css->StatDumpAddress,
                              &css->statcounters[0], 17 * 4);
                  if (CUCMD(val) == 7) { /* Reset stat counters */
                        memset(&css->statcounters[0], 0x00, 17 * 4);
                  }
                  break;
            };
            /* ----- RU Commands ----- */
            switch(RUCMD(val)) {
            case 0: /* NOOP */
                  break;
            case 1: /* RU Start */
                  css->RUPos = *LONGCARDREG(css, 0x04);
                  /* Fall through to resume! */
            case 2: /* RU Resume */
                  css->cardregs[0] &= ~SCB_STATUS_RUS;
                  css->cardregs[0] |= RUS_READY << SCB_STATUS_RUS_SHIFT;
                  break;
            case 4: /* RU Abort */
                  css->cardregs[0] &= ~SCB_STATUS_RUS;
                  css->cardregs[0] |= RUS_IDLE << SCB_STATUS_RUS_SHIFT;
                  break;
            case 5:     /* Load Header Data Size */
                  break;
            case 6: /* Load RU Base */
                  css->RUBase = *LONGCARDREG(css, 0x04);
                  TRACE(DEBUG_OTHER, "RUBase set to %08x\n", css->RUBase);
                  break;
            };
            return;
      case 0x03: /* Interrupt Control Byte */
            css->cardregs[port] = val & SCB_ICB_M;
            if (val & SCB_ICB_SI) { /* Request for Interrupt */
                  css->cardregs[1] |= SCB_STATACK_SWI;
            }
            chip_intel_82557_irq_update(css);
            return;
      case 0x0b: /* This ends an PORT write -> jump to the outl handler
                  which actually handles this */
            css->cardregs[port] = val;
            _chip_intel_82557_outl(css, 0x08, *LONGCARDREG(css, 0x08));
      case 0x0c: /* This seems to have something to do with the FLASH ROM */
            TRACE(DEBUG_FLASHROM, "FlashROM?-Register write: %02x\n", val);
            return;
      case 0x0e: /* Access to EEPROM control register */
            if (css->eeprom_selected != EEPROM_CSSIGNAL(val)) {
                  /* There has been a change in the selection - reset
                   * our internal status */
                  chip_intel_82557_reseteepromstatus(css);
                  css->eeprom_selected = EEPROM_CSSIGNAL(val);
            }
            if (EEPROM_SKSIGNAL(val) != (css->eeprom_clock)) {
                  /* Clock signal changed */
                  css->eeprom_clock = EEPROM_SKSIGNAL(val);
                  if (EEPROM_SKSIGNAL(val)) { /* Clock signal high */
                        if (css->eeprom_selected) {
                              chip_intel_82557_handleeepromclock(css,
                                          EEPROM_DISIGNAL(val));
                        }
                  }
            }
            if (css->eeprom_selected) {
                  if (EEPROM_OPCODEBITS <= css->eeprom_opcbits
                   && EEPROM_ADDRESSBITS <= css->eeprom_adbits) {
                        switch (css->eeprom_opcode & 0x03) {
                        case 0:     /* Special Functions like erase
                               * everything or write everything */
                              switch (css->eeprom_address >> 4) {
                              case 0:
                                    css->eeprom_ewenable = 0;
                                    break;
                              case 3:
                                    css->eeprom_ewenable = 1;
                                    break;
                              default:
                                    /* Erase everything and write
                                     * everything are
                                     * UNIMPLEMENTED */
                                    WARN("Unimplemented EEPROM "
                                          "command %d\n",
                                          css->eeprom_address >> 6);
                                    break;
                              };
                        case 1:     /* Write */
                              if (css->eeprom_ewenable) {
                                    chip_intel_82557_eepromwrite(css, &val);
                              }
                              break;
                        case 2:     /* Read */
                              chip_intel_82557_eepromread(css, &val);
                              break;
                        case 3:     /* Erase */
                              /* There is no data following the
                               * command, so set datapos */
                              if (css->eeprom_ewenable) {
                                    css->eeprom_data[css->eeprom_address] = 0;
                              }
                              css->eeprom_datapos = 17;
                              break;
                        };
                  } else {
                        val |= EEPROM_EEDO;
                  }
            }
            css->cardregs[port] = val;
            return;
      case 0x13: /* This ends an MDI write -> jump to the outl handler
                  which actually handles this */
            css->cardregs[port] = val;
            _chip_intel_82557_outl(css, 0x10, *LONGCARDREG(css, 0x10));
            return;
      case 0x14 ... 0x17: /* Early Receive Count Register */
            return; /* this register is READ ONLY. ignore the write. */
      default:
            TRACE(DEBUG_OTHER, "Port 0x%04x Value 0x%02x\n", port, val);
            if (port < 128) {
                  css->cardregs[port] = val;
            }
            return;
      };
}

static void
_chip_intel_82557_outw(struct css *css, uint16_t port, uint16_t val)
{
      switch (port) {
      case 0x00: /* Access to System Control Block */
      case 0x02: /* Access to System Control Block */
            /* Map to Byte access */
            _chip_intel_82557_outb(css, port + 0, (val & 0x00FF) >> 0);
            _chip_intel_82557_outb(css, port + 1, (val & 0xFF00) >> 8);
            return;
      case 0x0a: /* This ends an PORT write -> jump to the outl handler
                  who actually handles this */
            *SHORTCARDREG(css, port) = val;
            _chip_intel_82557_outl(css, 0x08, *LONGCARDREG(css, 0x08));
            return;
      case 0x0c: /* Access to FlashROM(?) control register */
            _chip_intel_82557_outb(css, port, val & 0xFF);
            /* The other 8 bits of this 16 bit space are unused,
               so we don't need to handle them */
            return;
      case 0x0e: /* Access to EEPROM control register */
            _chip_intel_82557_outb(css, port, val & 0xFF);
            /* The other 8 bits of this 16 bit space are unused,
               so we don't need to handle them */
            return;
      case 0x12: /* This ends an MDI write -> jump to the outl handler
                  who actually handles this */
            *SHORTCARDREG(css, port) = val;
            _chip_intel_82557_outl(css, 0x10, *LONGCARDREG(css, 0x10));
            return;
      case 0x14: /* Early Receive Count Register */
      case 0x16: /* Early Receive Count Register */
            return; /* this register is READ ONLY. ignore the write. */
      default:
            TRACE(DEBUG_OTHER, "Port 0x%04x Value 0x%04x\n", port, val);
            if (port < 64) {
                  *SHORTCARDREG(css, port) = val;
            }
            return;
      };
}

static void
_chip_intel_82557_outl(struct css *css, uint16_t port, uint32_t val)
{
      unsigned long selftestres;

      switch (port) {
      case 0x00: /* Access to System Control Block */
            _chip_intel_82557_outb(css, port + 0, (val & 0x000000FF) >>  0);
            _chip_intel_82557_outb(css, port + 1, (val & 0x0000FF00) >>  8);
            _chip_intel_82557_outb(css, port + 2, (val & 0x00FF0000) >> 16);
            _chip_intel_82557_outb(css, port + 3, (val & 0xFF000000) >> 24);
            return;
      case 0x08: /* the so called PORT register */
            TRACE(DEBUG_OTHER,
                  "PORT Command, Opcode=0x%x (%s) Address=0x%08x\n",
                  val & PORT_OPCODEMASK, PORTOpcodes[val&PORT_OPCODEMASK],
                  val & (~PORT_OPCODEMASK));
            switch (val & PORT_OPCODEMASK) {
            /* Note: Sequence is 0x01 - 0x00 - 0x02 because the self test
             * is followed by a full reset, and a full reset is a
             * selective reset plus something more */
            case 0x01: /* Self Test */
                  /* We write the self test results to the supplied
                   * memory address. */
                  /* Signature must not be zero */
                  selftestres = 0xffffffff;
                  chip_intel_82557_write(css,
                        val & ~PORT_OPCODEMASK, &selftestres, 4);
                  /* Result of all zeros means everything is fine. */
                  selftestres = 0;
                  chip_intel_82557_write(css,
                        (val & ~PORT_OPCODEMASK) + 4, &selftestres, 4);
                  /* don't generate an interrupt, and fall through to
                   * reset */
            case 0x00: /* Software Reset */
                  css->CUBase = 0;
                  css->CUPos = 0;
                  css->RUBase = 0;
                  css->RUPos = 0;
                  css->StatDumpAddress = 0;
                  memset(&css->statcounters[0], 0x00, 17*4);
                  memset(&css->mcasthash[0], 0x00, 8);
                  memcpy(&css->mac[0], &css->eeprom_data[0], 6);
                  chip_intel_82557_resetmdiregs(css);
                  chip_intel_82557_resetconfbytes(css);
                  /* fall through to selective reset */
            case 0x02: /* Selective Reset */
                  *LONGCARDREG(css, 0x00) = 0;
                  return;
            case 0x03: /* Dump */
                  return;
            default: /* We don't know what to do with this */
                  return;
            };
            return;
      case 0x0c: /* Access to EEPROM (or Flash?) control register */
            _chip_intel_82557_outb(css, port + 0, (val >>  0) & 0xff);
            _chip_intel_82557_outb(css, port + 2, (val >> 16) & 0xff);
            /* The other 24 bits of this 32 bit space are unused,
               so we don't need to handle them */
            return;

      case 0x10: /* MDI Control Register */
            TRACE(DEBUG_MDI,
                  "MDI Command, Value=0x%08x -> Opcode=%d (%s) "
                  "PHYAd=%d RegAd=%d\n",
                  val, MDICMD(val), MDICommands[MDICMD(val)],
                  MDIPHY(val), MDIREG(val));
            *LONGCARDREG(css, 0x10) &= MDI_DATA;
            *LONGCARDREG(css, 0x10) |= val & ~MDI_DATA;
            if (MDIPHY(val) == MDI_PHY_ADDR) {
                  /* it really is a command for us */
                  if (MDICMD(val) == 1) { /* Write */
                        unsigned long wmask;

                        wmask = MDI_DATA;
                        switch (MDIREG(val) & 31) {
                        case  0: wmask &= 0x00007dffUL; break;
                        case  4: wmask &= 0x0000a3ffUL; break;
                        case 16: wmask &= 0x0000f000UL; break;
                        default: wmask &= 0x00000000UL; break;
                        };
                        css->mdiregs[MDIREG(val) & 31] &= ~wmask;
                        css->mdiregs[MDIREG(val) & 31] |= val & wmask;
                  } else if (MDICMD(val) == 2) { /* Read */
                        /* clear data bits */
                        *LONGCARDREG(css, 0x10) &= ~MDI_DATA;
                        *LONGCARDREG(css, 0x10) |= css->mdiregs[MDIREG(val) & 31];
                  } else {
                        *LONGCARDREG(css, 0x10) &= ~MDI_DATA;
                        WARN("Illegal MDI Command (%ld) for PHY %ld\n",
                              (unsigned long) MDICMD(val),
                              (unsigned long) MDIPHY(val));
                  }
            } else {
                  *LONGCARDREG(css, 0x10) &= ~MDI_DATA;
                  TRACE(DEBUG_MDI,
                        "MDI Command (%ld) for nonexistant PHY %ld\n",
                        (unsigned long) MDICMD(val),
                        (unsigned long) MDIPHY(val));
            }
            /* MDI_READY is always set. See inl function */

            if (val & MDI_IE) {
                  css->cardregs[1] |= SCB_STATACK_MDI;
                  chip_intel_82557_irq_update(css);
            }
            return;
      case 0x14: /* Early Receive Count Register */
            /* This register is READ ONLY. Ignore the write. */
            return;
#ifdef __CARDBUS__
            /* FIXME cardbus */
      case 0x30:
            *LONGCARDREG(css, 0x30) &= ~(val & (1 << 15));
            return;
      case 0x34:
            *LONGCARDREG(css, 0x34) |= val & (1 << 15);
            return;
      case 0x38:
            /* read only */
            return;
      case 0x3C:
            if (val & (1 << 15)) {
                  *LONGCARDREG(css, 0x30) |= (1 << 15);
                  /* FIXME generate interrupt */
            }
            return;
#endif
      default:
            TRACE(DEBUG_OTHER, "Port 0x%04x value 0x%08x\n", port, val);
            if (port < 64) {
                  *LONGCARDREG(css, port)=val;
            }
            return;
      };
}

static void
_chip_intel_82557_ior(
      struct css *css,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      assert(! (addr & 3));
      assert(addr < sizeof(css->cardregs));

      *valp = 0;

      if ((bs >> 0) & 1) {
            *valp |= css->cardregs[addr + 0] << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= css->cardregs[addr + 1] << 8;
      }
      if ((bs >> 2) & 1) {
            *valp |= css->cardregs[addr + 2] << 16;
      }
      if ((bs >> 3) & 1) {
            *valp |= css->cardregs[addr + 3] << 24;
      }

      if (addr == 0x10) {
            /* FIXME */
            *valp |= MDI_READY;
      }
}

static int
chip_intel_82557_ior(void *_css, uint32_t addr, unsigned int bs, uint32_t *valp)
{
      struct css *css = (struct css *) _css;

      assert(! (addr & 3));

      if (addr < E100IOADDR(css)
       || E100IOADDR(css) + SZ_E100MEMORY <= addr
       || ! (css->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_IO)) {
            return -1;
      }

      addr &= (SZ_E100MEMORY - 1);

      _chip_intel_82557_ior(css, addr, bs, valp);

      return 0;
}

static void
_chip_intel_82557_iow(
      struct css *css,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      assert(! (addr & 3));
      assert(addr < sizeof(css->cardregs));

      /* FIXME */
      switch (bs) {
      case 0x1 << 0:
            _chip_intel_82557_outb(css, addr + 0, val >> 0);
            break;
      case 0x1 << 1:
            _chip_intel_82557_outb(css, addr + 1, val >> 8);
            break;
      case 0x1 << 2:
            _chip_intel_82557_outb(css, addr + 2, val >> 16);
            break;
      case 0x1 << 3:
            _chip_intel_82557_outb(css, addr + 3, val >> 24);
            break;
      case 0x3 << 0:
            _chip_intel_82557_outw(css, addr + 0, val >> 0);
            break;
      case 0x3 << 2:
            _chip_intel_82557_outw(css, addr + 2, val >> 16);
            break;
      case 0xf << 0:
            _chip_intel_82557_outl(css, addr + 0, val >> 0);
            break;
      default:
            assert(0);
      }
}

static int
chip_intel_82557_iow(void *_css, uint32_t addr, unsigned int bs, uint32_t val)
{
      struct css *css = (struct css *) _css;

      assert(! (addr & 3));

      if (addr < E100IOADDR(css)
       || E100IOADDR(css) + SZ_E100MEMORY <= addr
       || ! (css->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_IO)) {
            return -1;
      }

      addr &= (SZ_E100MEMORY - 1);

      _chip_intel_82557_iow(css, addr, bs, val);

      return 0;
}

static int
chip_intel_82557_mr(
      void *_css,
      uint32_t addr,
      unsigned int bs,
      uint32_t *valp
)
{
      struct css *css = (struct css *) _css;

      if ((css->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY) == 0) {
            return -1;
      }

      if (E100MEMADDR(css) <= addr
       && addr < E100MEMADDR(css) + SZ_E100MEMORY) {
            addr &= (SZ_E100MEMORY - 1);
            _chip_intel_82557_ior(css, addr, bs, valp);
            return 0;
      }
      if (E100FLASHADDR(css) <= addr
       && addr < E100FLASHADDR(css) + SZ_E100FLASHROM) {
            addr %= SZ_E100FLASHROM;
            *valp = 0;
            if ((bs >> 0) & 1) {
                  *valp |= css->flashrom[addr + 0] << 0;
            }
            if ((bs >> 1) & 1) {
                  *valp |= css->flashrom[addr + 1] << 8;
            }
            if ((bs >> 2) & 1) {
                  *valp |= css->flashrom[addr + 2] << 16;
            }
            if ((bs >> 3) & 1) {
                  *valp |= css->flashrom[addr + 3] << 24;
            }
            return 0;
      }
      if ((css->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE)
       && E100ROMADDR(css) <= addr
       && addr < E100ROMADDR(css) + SZ_E100ROMREQUEST) {
            addr %= SZ_E100ROMREQUEST;
            *valp = 0;
            if ((bs >> 0) & 1) {
                  *valp |= css->flashrom[addr + 0] << 0;
            }
            if ((bs >> 1) & 1) {
                  *valp |= css->flashrom[addr + 1] << 8;
            }
            if ((bs >> 2) & 1) {
                  *valp |= css->flashrom[addr + 2] << 16;
            }
            if ((bs >> 3) & 1) {
                  *valp |= css->flashrom[addr + 3] << 24;
            }
            return 0;
      }
      return -1;
}

static int
chip_intel_82557_mw(
      void *_css,
      uint32_t addr,
      unsigned int bs,
      uint32_t val
)
{
      struct css *css = (struct css *) _css;

      if ((css->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY) == 0) {
            return -1;
      }

      if (E100MEMADDR(css) <= addr
       && addr < E100MEMADDR(css) + SZ_E100MEMORY) {
            addr &= (SZ_E100MEMORY - 1);
            _chip_intel_82557_iow(css, addr, bs, val);
            return 0;
      }
      if (E100FLASHADDR(css) <= addr
       && addr < E100FLASHADDR(css) + SZ_E100FLASHROM) {
            /* FIXME fox: needs to be implemented -
             * if documentation can be found, or someone
             * figures it out. */
            addr %= SZ_E100FLASHROM;
            TRACE(DEBUG_FLASHROM,
                  "FlashSpace-WRITE: %ld bytes @%08lx: %08x\n", len, addr,
                  *(uint32_t *) &css->flashrom[addr]);
            return 0;
      }
      if (css->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE
       && E100ROMADDR(css) <= addr
       && addr < E100ROMADDR(css) + SZ_E100ROMREQUEST) {
            /* This is and always will be write protected.
             * Just ignore the write... */
            return 0;
      }
      return -1;
}

/* This will probably never be needed, we always use _read and _write... */
static int
chip_intel_82557_map(
      void *_css,
      unsigned long pa,
      unsigned int len,
      char **haddr_mr_p,
      char **haddr_mw_p
)
{
      struct css *css = (struct css *) _css;

      if ((css->config_space[PCI_COMMAND >> 2] & PCI_COMMAND_MEMORY) == 0) {
            return -1;
      }

      if (E100MEMADDR(css) != 0x0000
       && E100MEMADDR(css) <= pa
       && pa < E100MEMADDR(css) + SZ_E100MEMORY) {
            /* don't map but simulate access instead. */
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;
            return 0;
      }
      if (E100FLASHADDR(css) != 0x0000
       && E100FLASHADDR(css) <= pa
       && pa < E100FLASHADDR(css) + SZ_E100FLASHROM) {
            /* don't map but simulate access instead. */
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;
            return 0;
      }
      if (css->config_space[PCI_ROM_ADDRESS>>2] & PCI_ROM_ADDRESS_ENABLE
       && E100ROMADDR(css) <= pa
       && pa < E100ROMADDR(css) + SZ_E100ROMREQUEST) {
            /* don't map but simulate access instead. */
            *haddr_mr_p = NULL;
            *haddr_mw_p = NULL;
            return 0;
      }
      return 1;
}

static void
_chip_intel_82557_cwriteb(struct css *css, uint8_t val, unsigned long addr)
{
      unsigned char omem;
      unsigned char nmem;
      
      TRACE(DEBUG_CONFSPACE,
            "Confspace WRITEB: Address %02x, Value %02x\n",
            (unsigned int) (addr & 0xff), val);

      switch (addr) {
      case PCI_COMMAND:
            /* Just filter the hardwired-to-0 bits */
            val &= 0x47;
            omem = pci_getconfigb(css->config_space, addr & 0xff)
                  & PCI_COMMAND_MEMORY;
            pci_setconfigb(css->config_space, addr & 0xff, val);
            nmem = pci_getconfigb(css->config_space, addr & 0xff)
                  & PCI_COMMAND_MEMORY;
            if (omem != nmem) {
                  /* Re-map memory regions. */
                  sig_pci_bus_unmap(css->pci_bus, css,
                              E100MEMADDR(css), 4096);
                  sig_pci_bus_unmap(css->pci_bus, css,
                              E100FLASHADDR(css), SZ_E100ROMREQUEST);
                  sig_pci_bus_unmap(css->pci_bus, css,
                              css->config_space[PCI_ROM_ADDRESS >> 2]
                                    & PCI_ROM_ADDRESS_MASK,
                              SZ_E100ROMREQUEST);
            }
            break;
      case PCI_COMMAND + 1:
            /* Just filter the hardwired-to-0 bits */
            /* I'm not entirely sure whether Bit 1 ("Enable fast
             * back-to-back" should get filtered here. The documentation
             * says so, but at the same time it says we announce to be
             * FB2B capable, so I don't think we should. */
            val &= 0x03;
            pci_setconfigb(css->config_space, addr & 0xff, val);
            break;
      case PCI_STATUS:
            /* No writeable bits in this part of the status register. */
            break;
      case PCI_STATUS + 1:
            /* The bits in this part of the status register can be reset 
             * by writing 1 to them. */
            val = ~(val & 0xF7);
            val |= 0x02; /* But the devsel timing can't be changed */
            pci_setconfigb(css->config_space, addr & 0xff,
                  pci_getconfigb(css->config_space, addr & 0xff) & val);
            break;
      default:
            assert(0);
      };
}

static void
_chip_intel_82557_cwritel(struct css *css, unsigned char addr, uint32_t val)
{
      uint32_t oaddr;
      uint32_t naddr;

      TRACE(DEBUG_CONFSPACE,
            "Confspace WRITEL: Address %02x, Value %08x\n", addr, val);

      switch (addr) {
      case PCI_CACHE_LINE_SIZE: /* actually 4 * 8 bit registers */
            val &= 0x0000FF18; /* filter the bits that are hardwired to 0 */
            css->config_space[addr >> 2] = val;
            break;
      case PCI_COMMAND:  /* actually 2 * 16 bit registers */
            /* This access should never get here... */
            assert(0);
            break;
      case PCI_BASE_ADDRESS_0: /* Request 4 KB Memory space */
            oaddr = E100MEMADDR(css);
            css->config_space[addr >> 2] = pci_requestspace(val, 4096,
                              PCI_BASE_ADDRESS_SPACE_MEMORY
                              | PCI_BASE_ADDRESS_MEM_TYPE_32);
            naddr = E100MEMADDR(css);
            if (oaddr != naddr) {
                  /* Re-map old/new region. */
                  sig_pci_bus_unmap(css->pci_bus, css, oaddr, 4096);
                  sig_pci_bus_unmap(css->pci_bus, css, naddr, 4096);
            }
            TRACE(DEBUG_OTHER,
                  "Now Using MemAddr 0x%08x\n", E100MEMADDR(css));
            break;
      case PCI_BASE_ADDRESS_1: /* Request 32 IO Ports */
            css->config_space[addr >> 2] = pci_requestspace(val, 32,
                                    PCI_BASE_ADDRESS_SPACE_IO);
            TRACE(DEBUG_OTHER,
                  "Now Using IO 0x%08x\n", E100IOADDR(css));
            break;
      case PCI_BASE_ADDRESS_2: /* used by flash rom (request 1 MB) */
            oaddr = E100FLASHADDR(css);
            css->config_space[addr >> 2] = pci_requestspace(val,
                              SZ_E100ROMREQUEST,
                              PCI_BASE_ADDRESS_SPACE_MEMORY
                              | PCI_BASE_ADDRESS_MEM_TYPE_32);
            naddr = E100FLASHADDR(css);
            if (oaddr != naddr) {
                  /* Re-map old/new region. */
                  sig_pci_bus_unmap(css->pci_bus, css,
                              oaddr, SZ_E100ROMREQUEST);
                  sig_pci_bus_unmap(css->pci_bus, css,
                              naddr, SZ_E100ROMREQUEST);
            }
            TRACE(DEBUG_OTHER,
                  "Now Using Flash-MemAddr 0x%08x\n", E100FLASHADDR(css));
            break;
      case PCI_ROM_ADDRESS: /* ROM address (bootrom) */
            oaddr = css->config_space[PCI_ROM_ADDRESS >> 2]
                  & PCI_ROM_ADDRESS_MASK;
            css->config_space[addr>>2] = pci_requestspace(val,
                                          SZ_E100ROMREQUEST, 0);
            css->config_space[addr>>2] &= PCI_ROM_ADDRESS_MASK;
            css->config_space[addr>>2] |= val & PCI_ROM_ADDRESS_ENABLE;
            naddr = css->config_space[PCI_ROM_ADDRESS >> 2]
                  & PCI_ROM_ADDRESS_MASK;
            if (oaddr != naddr) {
                  /* Re-map new region. */
                  sig_pci_bus_unmap(css->pci_bus, css,
                              oaddr, SZ_E100ROMREQUEST);
                  sig_pci_bus_unmap(css->pci_bus, css,
                              naddr, SZ_E100ROMREQUEST);
            }
            TRACE(DEBUG_OTHER,
                  "Now Using ROM-Addr 0x%08x\n",
                  (uint32_t) (css->config_space[addr >> 2]
                            & PCI_ROM_ADDRESS_MASK));
            break;
      case PCI_INTERRUPT_LINE: /* actually 4 * 8 bit registers */
            /* only 8 bit of this are writeable */
            pci_setconfigb(css->config_space, addr, val & 0xff);
            break;
      case PCI_REVISION_ID:
      case PCI_BASE_ADDRESS_3: /* unused */
      case PCI_BASE_ADDRESS_4: /* unused */
      case PCI_BASE_ADDRESS_5: /* unused */
      case 0x28:         /* reserved */
      case PCI_VENDOR_ID:      /* 16 bit Vendor ID and 16 bit Device ID */
      case PCI_SUBSYSTEM_VENDOR_ID: /* 2*16 bit subsys v.-id and subsystemid */
      case PCI_CAPABILITY_LIST:/* not supported */
      case 0x38:         /* reserved */
      case 0x40 ... 0xfc:      /* unused/reserved */
            /* All the registers above are write-protected or
             * hard-wired to 0. That's why we ignore the write. */
            break;
      default:
            TRACE(DEBUG_CONFSPACE,
                  "Warning: Unknown Register written: %02x: %08x\n",
                  addr, val);
            break;
      };
}

static int
chip_intel_82557_c0w(void *_css, uint32_t addr, unsigned int bs, uint32_t val)
{
      struct css *const css = (struct css *) _css;

      assert (! (addr & 3));

      addr &= 0x7ff;
      if ((addr >> 8) & 7) {
            return -1;
      }
      addr &= 0xff;

      switch (addr) {
      case PCI_COMMAND:
            if ((bs >> 0) & 1) {
                  _chip_intel_82557_cwriteb(css, val >>  0, addr + 0);
            }
            if ((bs >> 1) & 1) {
                  _chip_intel_82557_cwriteb(css, val >>  8, addr + 1);
            }
            if ((bs >> 2) & 1) {
                  _chip_intel_82557_cwriteb(css, val >>  16, addr + 2);
            }
            if ((bs >> 3) & 1) {
                  _chip_intel_82557_cwriteb(css, val >>  24, addr + 3);
            }
            break;
      default:
            /* FIXME */
            if (! ((bs >> 0) & 1)) {
                  val &= ~(0xff << 0);
                  val |= pci_getconfigb(css->config_space, addr + 0) << 0;
            }
            if (! ((bs >> 1) & 1)) {
                  val &= ~(0xff << 8);
                  val |= pci_getconfigb(css->config_space, addr + 0) << 8;
            }
            if (! ((bs >> 2) & 1)) {
                  val &= ~(0xff << 16);
                  val |= pci_getconfigb(css->config_space, addr + 0) << 16;
            }
            if (! ((bs >> 3) & 1)) {
                  val &= ~(0xff << 24);
                  val |= pci_getconfigb(css->config_space, addr + 0) << 24;
            }
            _chip_intel_82557_cwritel(css, addr, val);
            break;
      }
      return 0;
}

static int
chip_intel_82557_c0r(void *_css, uint32_t addr, unsigned int bs, uint32_t *valp)
{
      struct css *const css = (struct css *) _css;
      
      assert(! (addr & 3));

      addr &= 0x7ff;
      if ((addr >> 8) & 7) {
            return -1;
      }
      addr &= 0xff;

      *valp = 0;
      if ((bs >> 0) & 1) {
            *valp |= pci_getconfigb(css->config_space, addr + 0) << 0;
      }
      if ((bs >> 1) & 1) {
            *valp |= pci_getconfigb(css->config_space, addr + 1) << 8;
      }
      if ((bs >> 2) & 1) {
            *valp |= pci_getconfigb(css->config_space, addr + 2) << 16;
      }
      if ((bs >> 3) & 1) {
            *valp |= pci_getconfigb(css->config_space, addr + 3) << 24;
      }
      return 0;
}

static void
chip_intel_82557_recv(void *_css, const void *buf, unsigned int buflen)
{
      struct css *css = (struct css *) _css;

      if (! css->state_power
       || chip_intel_82557_checkloopbackmode(css)) {
            return;
      }

      assert(buflen <= sizeof(css->tmprpacket));
      memcpy(css->tmprpacket, buf, buflen);
      chip_intel_82557_handlereceivedpacket(css, buflen);
}

static void
chip_intel_82557_reset(struct css *css)
{
      /* 
       * Naive approach of a Card Information Structure.
       * Unfortunately i have no real CardBus EEPro100 so some
       * entries should be considered as wild speculations.
       */
      /* FIXME waldi: explain all tuple byte values */
      static const uint8_t cis_eepro100[] = {
            
            0x13, /* CISTPL_LINKTARGET */
            0x03, /* link to next tuple, always follows the tuple type
                   except for null tuples CISTPL_NULL */
            0x43, /* "C" */
            0x49, /* "I" */
            0x53, /* "S" */

            0x04, /* CISTPL_CONFIG_CB */
            0x06,
            0x03,
            0x40,
            0x00,
            0x00,
            0x00,
            0x00,

            0x05, /* CISTPL_CFTABLE_ENTRY_CB */
            0x05,
            0x40,
            0x38,
            0x02,
            0xA0,
            0x02,

            0x07, /* CISTPL_BAR */
            0x06,
            0x01,
            0x00,
            0x00,
            0x00,
            0x10,
            0x00,

            0x07, /* CISTPL_BAR */
            0x06,
            0x12,
            0x00,
            0x00,
            0x00,
            0x00,
            0x20,

            0x07, /* CISTPL_BAR */
            0x06,
            0x07,
            0x00,
            0x00,
            0x10,
            0x00,
            0x00,
            
            0x15, /* CISTPL_VERS_1 */
            0x0b,
            0x07,
            0x00,
            0x49, /* "I" */
            0x6E, /* "n" */
            0x74, /* "t" */
            0x65, /* "e" */
            0x6C, /* "l" */
            0x00, 
            0x00, /* skip the rest */
            0x00, /* and see what happens */
            0x00,

            0x20, /* CISTPL_MANFID */
            0x04,
            0x89, /* I found this somewhere and hope it's correct */
            0x00, /* Intel Manf. Id 0089, Prod. Id 0102 */
            0x02,
            0x01,

            0x1c, /* CISTPL_DEVICE_OC */
            0x03,
            0x03, /* other conditions info */
            0x47, /* EEPROM */
            0x0D, /* 2 * 512 KB */

            0xff, /* CISTPL_END */
      };
      
      /* initialize/reset PCI config space */
      memset(css->config_space, 0, 0x100);
      pci_setconfigw(css->config_space,
            PCI_VENDOR_ID, PCI_VENDOR_ID_INTEL);
      pci_setconfigw(css->config_space,
            PCI_DEVICE_ID, PCI_DEVICE_ID_INTEL_82557);
      pci_setconfigw(css->config_space,
            PCI_COMMAND, PCI_COMMAND_MASTER);
      pci_setconfigw(css->config_space,
            PCI_STATUS,
            PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK);
      pci_setconfigb(css->config_space,
            PCI_REVISION_ID, 0x01);
      pci_setconfigw(css->config_space,
            PCI_CLASS_DEVICE, PCI_CLASS_NETWORK_ETHERNET);
      pci_setconfigb(css->config_space, PCI_LATENCY_TIMER, 32);
      pci_setconfigl(css->config_space,
            PCI_BASE_ADDRESS_0,
            (PCI_BASE_ADDRESS_SPACE_MEMORY
            | PCI_BASE_ADDRESS_MEM_TYPE_32));
      pci_setconfigl(css->config_space,
            PCI_BASE_ADDRESS_1,
            PCI_BASE_ADDRESS_SPACE_IO);
      /* CIS Pointer: CIS is in config space, offset 0x40 */
      pci_setconfigl(css->config_space, 0x28, 0x40 << 3);
      /* 8086 200d  EtherExpress PRO/100 Cardbus */
      pci_setconfigw(css->config_space,
            PCI_SUBSYSTEM_VENDOR_ID, PCI_VENDOR_ID_INTEL);
      pci_setconfigw(css->config_space,
            PCI_SUBSYSTEM_ID, 0x200d);
      pci_setconfigb(css->config_space,
            PCI_INTERRUPT_PIN, PCI_INT_A);
      pci_setconfigb(css->config_space, PCI_MIN_GNT, 0x08);
      pci_setconfigb(css->config_space, PCI_MAX_LAT, 0x18);

      assert(sizeof(cis_eepro100) < sizeof(css->config_space) - 0x40);
      memcpy(&css->config_space[0x40], &cis_eepro100, sizeof(cis_eepro100));

      /* And reset the cards registers */
      memset(css->cardregs, 0, sizeof(css->cardregs));
}

static void
chip_intel_82557_power_set(void *_css, unsigned int val)
{
      struct css *css = (struct css *) _css;

      css->state_power = val;

      if (val) {
            chip_intel_82557_reset(css);
      }
}

static void
chip_intel_82557_n_reset_set(void *_css, unsigned int n_val)
{
      struct css *css = (struct css *) _css;

      if (n_val) {
            chip_intel_82557_reset(css);
      }
}

#if 0
static void
chip_intel_82557_tick(void *_css)
{
      struct css *css = (struct css *) _css;
      int active;

      active = css->active;
      css->active = 0;

      sig_boolean_set(css->sig_busy, css, active);

      time_call_after(TIME_HZ / 2, chip_intel_82557_tick, css);
}
#endif

void
chip_intel_82557_cardbus_init(
      unsigned int nr,
      struct sig_boolean *sig_power,
      struct sig_boolean *sig_n_reset,
      struct sig_pci_bus_idsel *sig_idsel,
      struct sig_pci_bus_main *pci_bus,
      struct sig_boolean_or *pci_bus_intA,
      struct sig_eth *sig_eth,
      struct sig_boolean *sig_busy
)
{
      static const struct sig_boolean_funcs power_funcs = {
            .set = chip_intel_82557_power_set,
      };
      static const struct sig_boolean_funcs n_reset_funcs = {
            .set = chip_intel_82557_n_reset_set,
      };
      static const struct sig_pci_bus_idsel_funcs idsel_funcs = {
            .c0r =            chip_intel_82557_c0r,
            .c0w =            chip_intel_82557_c0w,
      };
      static const struct sig_pci_bus_main_funcs funcs = {
            .ior =            chip_intel_82557_ior,
            .iow =            chip_intel_82557_iow,

            .mr =       chip_intel_82557_mr,
            .mw =       chip_intel_82557_mw,
            .map =            chip_intel_82557_map,
      };
      static const struct sig_eth_funcs eth_funcs = {
            .recv =           chip_intel_82557_recv,
      };

      struct css *css;

      css = shm_map(COMP, nr, sizeof(*css), 0);

      css->sig_power = sig_power;
      sig_boolean_connect_in(sig_power, css, &power_funcs);

      css->sig_n_reset = sig_n_reset;
      sig_boolean_connect_in(sig_n_reset, css, &n_reset_funcs);

      sig_pci_bus_idsel_connect(sig_idsel, css, &idsel_funcs);

      css->pci_bus = pci_bus;
      sig_pci_bus_main_connect(pci_bus, css, &funcs);

      css->pci_int = pci_bus_intA;
      sig_boolean_or_connect_out(pci_bus_intA, css, 0);

      css->sig_eth = sig_eth;
      sig_eth_connect(sig_eth, css, &eth_funcs);

      css->sig_busy = sig_busy;
      sig_boolean_connect_out(sig_busy, css, 0);

#if 0
      time_call_after(TIME_HZ / 2, chip_intel_82557_tick, css);
#endif
}

static void
chip_intel_82557_fixeepromchecksum(struct css *css)
{
      unsigned short sum;
      int i;

      sum = 0;
      for (i = 0; i < 63; i++) {
            sum += css->eeprom_data[i];
      }
      css->eeprom_data[63] = 0xbaba - sum;
}

unsigned int
chip_intel_82557_cardbus_create(const char *mac)
{
      static unsigned int nr = 0;
      struct css *css;
      char fn[FILENAME_MAX];
      int fd;
      int ret;

      shm_create(COMP, nr, sizeof(*css));
      css = shm_map(COMP, nr, sizeof(*css), 0);

      memset(css, 0, sizeof(*css));

      css->nr = nr;

      if (mac == NULL) {
            mac = "00:00:00:00:00:00";
      }

      /* initialize eeprom */
      memset(css->eeprom_data, 0, SZ_E100EEPROM);
      sprintf(css->eeprom_filename, "%s/eepro100-%d-eeprom", basedir, nr);
      fd = open(css->eeprom_filename, O_RDONLY);
      if (0 <= fd) { /* Load file */
            ret = read(fd, css->eeprom_data, SZ_E100EEPROM * 2);
            assert(ret == SZ_E100EEPROM * 2);
            ret = close(fd);
            assert(0 <= ret);
      } else {
            int m[6];

            ret = sscanf(mac, "%x:%x:%x:%x:%x:%x",
                        &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]);
            css->eeprom_data[0] = (m[1] << 8) | m[0];
            css->eeprom_data[1] = (m[3] << 8) | m[2];
            css->eeprom_data[2] = (m[5] << 8) | m[4];
            /* eepro100.c uses that bits to detect a "receiver
             * lock-up bug" - well lets make it happy by telling
             * it that we are not buggy. */
            css->eeprom_data[3] = 0x0003;
            /* only a RJ45 connector */
            css->eeprom_data[5] = 0x0001;
            /* Phys. Layer = i82555 */
            css->eeprom_data[6] = 0x0700 | MDI_PHY_ADDR;
            /* secondary int. chip i82555 */
            css->eeprom_data[7] = 0x0700;
            chip_intel_82557_fixeepromchecksum(css);
      }
      /* initialize flash rom */
      sprintf(fn, "%s/eepro100-%d-flashrom", basedir, nr);
      fd = open(fn, O_RDONLY);
      if (0 <= fd) { /* Load file */
            ret = read(fd, css->flashrom, SZ_E100FLASHROM);
            assert(ret == SZ_E100FLASHROM);
            ret = close(fd);
            assert(0 <= ret);
      } else {
            memset(css->flashrom, 0, SZ_E100FLASHROM);
      }

      shm_unmap(css, sizeof(*css));

      return nr++;
}

void
chip_intel_82557_cardbus_destroy(unsigned int nr)
{
      struct css *css;

      css = shm_map(COMP, nr, sizeof(*css), 0);

      shm_unmap(css, sizeof(*css));
      shm_destroy(COMP, nr);
}

Generated by  Doxygen 1.6.0   Back to index