Hello,

I'm trying to port the SiS190 driver from freeBSD. So far I've been 
quite successful. It compiles without warnings and finds the the MAC and 
in my case the Realtek PHY. Sadly I only have one PC with one NIC so I 
can do no real testing. My dsl modem with PPPoE does not yet work. Still 
I can see some activity on the link when I try to set it up. The driver 
is attached. It still needs some cleanup, but first I want to get
working. Please read on if you have only little knowledge about the pci
support in the openBSD kernel. I have none and probably only need a
small hint.

It probably doesn't yet work because the driver is not able to read the 
MAC address. On linux the chip works and on dmesg I read:

>     sis190 Gigabit Ethernet driver 1.3 loaded.
>       alloc irq_desc for 19 on node -1
>       alloc kstat_irqs on node -1
>     sis190 0000:00:04.0: PCI INT A -> GSI 19 (level, low) -> IRQ 19
>     sis190 0000:00:04.0: setting latency timer to 64
>     0000:00:04.0: Read MAC address from EEPROM
>     0000:00:04.0: Error EEPROM read 0.
Same as above on my ported driver for openBSD.
>     0000:00:04.0: Read MAC address from APC.
>     0000:00:04.0: Realtek PHY RTL8201 transceiver at address 

the relevant parts from the linux driver for "Read MAC address from APC" 
can be found below for you convenience. Now my problem is I have very 
little BSD experience. I now have used it only for four days and have no 
clue how to port this function. And weather similar functions/macros 
exist in openBSD. Any little hint is appreciated. Thanks.


Cheers,

Christopher Zimmermann


rc = sis190_get_mac_addr_from_eeprom(pdev, dev); /* This seems to fail */
if (rc < 0) {
    u8 reg;

    pci_read_config_byte(pdev, 0x73, &reg); /* How to do this in openBSD */

    if (reg & 0x00000001)
            rc = sis190_get_mac_addr_from_apc(pdev, dev);
}

/**
 *      sis190_get_mac_addr_from_apc - Get MAC address for SiS96x model
 *      @pdev: PCI device
 *      @dev:  network device to get address for
 *
 *      SiS96x model, use APC CMOS RAM to store MAC address.
 *      APC CMOS RAM is accessed through ISA bridge.
 *      MAC address is read into @net_dev->dev_addr.
 */
static int __devinit sis190_get_mac_addr_from_apc(struct pci_dev *pdev,
                                                  struct net_device *dev)
{
        static const u16 __devinitdata ids[] = { 0x0965, 0x0966, 0x0968 };
        struct sis190_private *tp = netdev_priv(dev);
        struct pci_dev *isa_bridge;
        u8 reg, tmp8;
        unsigned int i;

        net_probe(tp, KERN_INFO "%s: Read MAC address from APC.\n",
                  pci_name(pdev));

/*
 * Now this is the interesting part. How do I do this on openBSD?
 * Here's the linux source for pc_get_device:
 * 
 **
 * pci_get_device - begin or continue searching for a PCI device by 
vendor/device id
 * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
 * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
 * @from: Previous PCI device found in search, or %NULL for new search.
 *
 * Iterates through the list of known PCI devices.  If a PCI device is
 * found with a matching @vendor and @device, the reference count to the
 * device is incremented and a pointer to its device structure is returned.
 * Otherwise, %NULL is returned.  A new search is initiated by passing %NULL
 * as the @from argument.  Otherwise if @from is not %NULL, searches continue
 * from next device on the global list.  The reference count for @from is
 * always decremented if it is not %NULL.
 */
struct pci_dev *
pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
{
        return pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);
}
        for (i = 0; i < ARRAY_SIZE(ids); i++) {
                isa_bridge = pci_get_device(PCI_VENDOR_ID_SI, ids[i], 
                NULL);
                if (isa_bridge)
                        break;
        }

        if (!isa_bridge) {
                net_probe(tp, KERN_INFO "%s: Can not find ISA bridge.\n",
                          pci_name(pdev));
                return -EIO;
        }

        /* Enable port 78h & 79h to access APC Registers. */
        pci_read_config_byte(isa_bridge, 0x48, &tmp8);
        reg = (tmp8 & ~0x02);
        pci_write_config_byte(isa_bridge, 0x48, reg);
        udelay(50);
        pci_read_config_byte(isa_bridge, 0x48, &reg);

        for (i = 0; i < MAC_ADDR_LEN; i++) {
                outb(0x9 + i, 0x78);
                dev->dev_addr[i] = inb(0x79);
        }

        outb(0x12, 0x78);
        reg = inb(0x79);

        sis190_set_rgmii(tp, reg);

        /* Restore the value to ISA Bridge */
        pci_write_config_byte(isa_bridge, 0x48, tmp8);
        pci_dev_put(isa_bridge);

        return 0;
}
/*-
 * Copyright (c) 2007, 2008 Alexander Pohoyda <alexander.poho...@gmx.net>
 * Copyright (c) 1997, 1998, 1999
 *	Bill Paul <wp...@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL AUTHORS OR
 * THE VOICES IN THEIR HEADS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>

/*
 * SiS 190 Fast Ethernet PCI NIC driver.
 *
 * Adapted to SiS 190 NIC by Alexander Pohoyda based on the original
 * SiS 900 driver by Bill Paul, using SiS 190/191 Solaris driver by
 * Masayuki Murayama and SiS 190/191 GNU/Linux driver by K.M. Liu
 * <km...@sis.com>.  Thanks to Pyun YongHyeon <pyu...@gmail.com> for
 * review and very useful comments.
 *
 * It should be easy to adapt this driver to SiS 191 Gigabit Ethernet
 * PCI NIC.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/sockio.h>
#include <sys/endian.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>

#include <net/if.h>
#include <net/if_arp.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#include <netinet/if_ether.h>
#endif

#include <net/bpf.h>

#include <machine/bus.h>

#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include "if_sis19xreg.h"

/* "controller miibus0" required.  See GENERIC if you get errors here. */

/*
 * Various supported device vendors/types and their names.
 */
static const struct pci_matchid sis_devices[] = {
	{ PCI_VENDOR_SIS, PCI_PRODUCT_SIS_190 },
	/*{ PCI_VENDOR_SIS, PCI_PRODUCT_SIS_191 }*/
};

static int sis_probe(struct device *, void *, void *);
static void sis_attach(struct device *, struct device *, void *);

struct cfattach sis19x_ca = {
	sizeof(struct sis_softc), sis_probe, sis_attach
};

struct cfdriver sis19x_cd = {
	0, "sis19x", DV_IFNET
};

static void sis_shutdown	(void *);

static int sis_miibus_readreg	(struct device *, int, int);
static void sis_miibus_writereg	(struct device *, int, int, int);
static void sis_miibus_statchg	(struct device *);

static int sis_newbuf		(struct sis_softc *,
					u_int32_t, struct mbuf *);
static int sis_encap		(struct sis_softc *,
					struct mbuf *, u_int32_t *);
static void sis_rxeof		(struct sis_softc *);
static void sis_txeof		(struct sis_softc *);
static int sis_intr		(void *);
static void sis_tick		(void *);
static void sis_start		(struct ifnet *);
static int sis_ioctl		(struct ifnet *, u_long, caddr_t);
static int sis_init		(struct ifnet *);
static void sis_stop		(struct sis_softc *);
static void sis_watchdog	(struct ifnet *);
static int sis_ifmedia_upd	(struct ifnet *);
static void sis_ifmedia_sts	(struct ifnet *, struct ifmediareq *);

static int sis_get_mac_addr_eeprom(struct sis_softc *, caddr_t);
static uint16_t sis_read_eeprom	(struct sis_softc *, int);

static void sis_setmulti	(struct sis_softc *);
static void sis_reset		(struct sis_softc *);
static int sis_list_rx_init	(struct sis_softc *);
static int sis_list_rx_free	(struct sis_softc *);
static int sis_list_tx_init	(struct sis_softc *);
static int sis_list_tx_free	(struct sis_softc *);
static uint32_t sis_mchash	(struct sis_softc *, const uint8_t *);

#define SIS_PCI_LOMEM		0x10
#if 0
static struct resource_spec sis19x_res_spec[] = {
	{ SYS_RES_MEMORY,	0x10,	RF_ACTIVE},
	{ SYS_RES_IRQ,		0x00,	RF_ACTIVE | RF_SHAREABLE},
	{ -1, 0 }
};

static device_method_t sis19x_methods[] = {
	/* Device interface */
	DEVMETHOD(device_probe,		sis_probe),
	DEVMETHOD(device_attach,	sis_attach),
	DEVMETHOD(device_detach,	sis_detach),
	DEVMETHOD(device_shutdown,	sis_shutdown),

	/* Bus interface */
	DEVMETHOD(bus_print_child,	bus_generic_print_child),
	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),

	/* MII interface */
	DEVMETHOD(miibus_readreg,	sis_miibus_readreg),
	DEVMETHOD(miibus_writereg,	sis_miibus_writereg),

	{ 0, 0 }
};

static driver_t sis19x_driver = {
	"sis19x", sis19x_methods, sizeof(struct sis_softc)
};
#endif

enum sis19x_registers {
	TxControl		= 0x00,
	TxDescStartAddr		= 0x04,
	Reserved0		= 0x08, // unused
	TxNextDescAddr		= 0x0c,	// unused

	RxControl		= 0x10,
	RxDescStartAddr		= 0x14,
	Reserved1		= 0x18, // unused
	RxNextDescAddr		= 0x1c,	// unused

	IntrStatus		= 0x20,
	IntrMask		= 0x24,
	IntrControl		= 0x28,
	IntrTimer		= 0x2c,	// unused

	PMControl		= 0x30,	// unused
	Reserved2		= 0x34, // unused
	ROMControl		= 0x38,
	ROMInterface		= 0x3c,
	StationControl		= 0x40,
	GMIIControl		= 0x44,
	GMacIOCR		= 0x48,
	GMacIOCTL		= 0x4c,
	TxMacControl		= 0x50,
	TxMacTimeLimit		= 0x54,
	RGMIIDelay		= 0x58,
	Reserved3		= 0x5c,
	RxMacControl		= 0x60, // 1  WORD
	RxMacAddr		= 0x62, // 6x BYTE
	RxHashTable		= 0x68, // 1 LONG
	RxHashTable2		= 0x6c, // 1 LONG
	RxWakeOnLan		= 0x70,
	RxWakeOnLanData		= 0x74,
	RxMPSControl		= 0x78,
	Reserved4		= 0x7c,
};

enum sis19x_register_content {
	/* IntrStatus */
	SoftInt			= 0x40000000,	// unused
	Timeup			= 0x20000000,	// unused
	PauseFrame		= 0x00080000,	// unused
	MagicPacket		= 0x00040000,	// unused
	WakeupFrame		= 0x00020000,	// unused
	LinkChange		= 0x00010000,
	RxQEmpty		= 0x00000080,			//! RXIDLE
	RxQInt			= 0x00000040,			//! RXDONE
	TxQ1Empty		= 0x00000020,	// unused
	TxQ1Int			= 0x00000010,	// unused
	TxQEmpty		= 0x00000008,			//! TXIDLE
	TxQInt			= 0x00000004,			//! TXDONE
	RxHalt			= 0x00000002,			//! RXHALT
	TxHalt			= 0x00000001,			//! TXHALT

	/* RxStatusDesc */
	RxRES			= 0x00200000,	// unused
	RxCRC			= 0x00080000,
	RxRUNT			= 0x00100000,	// unused
	RxRWT			= 0x00400000,	// unused

	/* {Rx/Tx}CmdBits */
	CmdReset		= 0x10,
	CmdRxEnb		= 0x01, /* Linux does not use it, but 0x8 */
	CmdTxEnb		= 0x01,

	/* RxMacControl */
	AcceptBroadcast		= 0x0800,
	AcceptMulticast		= 0x0400,
	AcceptMyPhys		= 0x0200,
	AcceptAllPhys		= 0x0100,
	AcceptErr		= 0x0020,	// unused
	AcceptRunt		= 0x0010,	// unused
};

/* fix KASSERT for two arguments */
#ifdef KASSERT
#undef KASSERT
#endif
#define KASSERT(cond, complaint) if (!(cond)) panic complaint

/*
 * register space access macros
 */
#define CSR_WRITE_4(sc, reg, val)	\
	bus_space_write_4(sc->sis_btag, sc->sis_bhandle, reg, val)
#define CSR_WRITE_2(sc, reg, val)	\
	bus_space_write_2(sc->sis_btag, sc->sis_bhandle, reg, val)

#define CSR_READ_4(sc, reg)	\
	bus_space_read_4(sc->sis_btag, sc->sis_bhandle, reg)
#define CSR_READ_2(sc, reg)	\
	bus_space_read_2(sc->sis_btag, sc->sis_bhandle, reg)

#define SIS_PCI_COMMIT()	CSR_READ_4(sc, IntrControl)

#define SIS_SETBIT(_sc, _reg, x)				\
	CSR_WRITE_4(_sc, _reg, CSR_READ_4(_sc, _reg) | (x))

#define SIS_CLRBIT(_sc, _reg, x)				\
	CSR_WRITE_4(_sc, _reg, CSR_READ_4(_sc, _reg) & ~(x))

#define DISABLE_INTERRUPTS(sc)	CSR_WRITE_4(sc, IntrMask, 0x0)
#define ENABLE_INTERRUPTS(sc)	CSR_WRITE_4(sc, IntrMask, RxQEmpty | RxQInt | \
					    TxQInt | RxHalt | TxHalt)

#define DEBUG
#if defined(DEBUG)
#define SIS_DEBUG(str, args...)	\
	device_printf(sc->sis_self, "%s: " str "\n", __func__, ## args)
#else
#define SIS_DEBUG(str, args...)
#endif

#define SIS_OUT(str, args...)	\
	printf(str "\n", ## args)


/*
 * Gigabit Media Independent Interface CTL register
 */
#define	GMI_DATA	0xffff0000
#define	GMI_DATA_SHIFT	16
#define	GMI_REG		0x0000f800
#define	GMI_REG_SHIFT	11
#define	GMI_PHY		0x000007c0
#define	GMI_PHY_SHIFT	6
#define	GMI_OP		0x00000020
#define	GMI_OP_SHIFT		5
#define	GMI_OP_WR	(1 << GMI_OP_SHIFT)
#define	GMI_OP_RD	(0 << GMI_OP_SHIFT)
#define	GMI_REQ		0x00000010
#define	GMI_MDIO	0x00000008 	/* not used */
#define	GMI_MDDIR	0x00000004	/* not used */
#define	GMI_MDC		0x00000002	/* not used */
#define	GMI_MDEN	0x00000001	/* not used */

enum CommandStatus {
	OWNbit		= 0x80000000,
	INTbit		= 0x40000000,
	IPbit		= 0x20000000,
	TCPbit		= 0x10000000,
	UDPbit		= 0x08000000,
	DEFbit		= 0x00200000,
	CRCbit		= 0x00020000,
	PADbit		= 0x00010000,
};


/*
 * RX descriptor status bits
 */
#define	RDS_TAGON	0x80000000
#define	RDS_DESCS	0x3f000000
#define	RDS_ABORT	0x00800000
#define	RDS_SHORT	0x00400000
#define	RDS_LIMIT	0x00200000
#define	RDS_MIIER	0x00100000
#define	RDS_OVRUN	0x00080000
#define	RDS_NIBON	0x00040000
#define	RDS_COLON	0x00020000
#define	RDS_CRCOK	0x00010000
#define	RX_ERR_BITS \
	(RDS_COLON | RDS_NIBON | RDS_OVRUN | RDS_MIIER | \
	 RDS_LIMIT | RDS_SHORT | RDS_ABORT)

#define RING_END		0x80000000

#define SIS_RXSIZE(x)		letoh32((x)->sis_sts_size & 0x0000ffff)
#define SIS_RXSTATUS(x)		letoh32((x)->sis_sts_size & 0xffff0000)

#undef SIS_OWNDESC
#define SIS_OWNDESC(x)		((x)->sis_cmdsts & OWNbit)

#define SIS_INC(x, y)		(x) = (((x) == ((y)-1)) ? 0 : (x)+1)


/*
 * TX descriptor status bits
 */
#define	TDS_OWC		0x00080000
#define	TDS_ABT		0x00040000
#define	TDS_FIFO	0x00020000
#define	TDS_CRS		0x00010000
#define	TDS_COLLS	0x0000ffff
#define TX_ERR_BITS 	(TDS_OWC | TDS_ABT | TDS_FIFO | TDS_CRS)

/* Taken from Solaris driver */
#define	EI_DATA		0xffff0000 
#define	EI_DATA_SHIFT	16
#define	EI_OFFSET	0x0000fc00
#define	EI_OFFSET_SHIFT	10
#define	EI_OP		0x00000300 
#define	EI_OP_SHIFT	8 
#define	EI_OP_RD	(2 << EI_OP_SHIFT)
#define	EI_OP_WR	(1 << EI_OP_SHIFT)
#define	EI_REQ		0x00000080
#define	EI_DO		0x00000008	/* not used */
#define	EI_DI		0x00000004	/* not used */
#define	EI_CLK		0x00000002	/* not used */
#define	EI_CS		0x00000001

/*
 * EEPROM Addresses
 */
#define	EEPROMSignature	0x00
#define	EEPROMCLK	0x01
#define	EEPROMInfo	0x02
#define	EEPROMMACAddr	0x03


/*
 * Read a sequence of words from the EEPROM.
 */
static uint16_t
sis_read_eeprom(sc, offset)
	struct sis_softc	*sc;
	int			offset;
{
	uint32_t	ret, val, i;

	KASSERT(offset <= EI_OFFSET, ("EEPROM offset too big"));

	val = EI_REQ | EI_OP_RD | (offset << EI_OFFSET_SHIFT);
	CSR_WRITE_4(sc, ROMInterface, val);
	DELAY(500);

	for (i = 0; ((ret = CSR_READ_4(sc, ROMInterface)) & EI_REQ) != 0; i++) {
		if (i > SIS_TIMEOUT) {
			/* timeout */
		  SIS_OUT("EEPROM read timeout %d", i);
			return (0xffff);
		}
		DELAY(100);
	}

	return ((ret & EI_DATA) >> EI_DATA_SHIFT);
}

static int
sis_get_mac_addr_eeprom(sc, dest)
	struct sis_softc	*sc;
	caddr_t			dest;
{
	uint16_t	val, i;

	val = sis_read_eeprom(sc, EEPROMSignature);
	if (val == 0xffff || val == 0x0000) {
		SIS_OUT("invalid EEPROM signature (%x)", val);
		return (1);
	}

	for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
		val = sis_read_eeprom(sc, EEPROMMACAddr + i/2);
		dest[i + 0] = (uint8_t) val;
		dest[i + 1] = (uint8_t) (val >> 8);
	}

	return (0);
}

static void
miibus_cmd(struct sis_softc *sc, u_int32_t ctl)
{
	uint32_t	i, ret;

	CSR_WRITE_4(sc, GMIIControl, ctl);
	DELAY(10);

	for (i = 0; ((ret = CSR_READ_4(sc, GMIIControl)) & GMI_REQ) != 0; i++) {
		if (i > SIS_TIMEOUT) {
			/* timeout */
			SIS_OUT("MIIBUS timeout");
			return;
		}
		DELAY(10);
	}
}

static int
sis_miibus_readreg(self, phy, reg)
	struct device		*self;
	int			phy, reg;
{
	struct sis_softc	*sc = (struct sis_softc *)self;
	miibus_cmd(sc, (phy << GMI_PHY_SHIFT) |
		(reg << GMI_REG_SHIFT) | GMI_OP_RD | GMI_REQ);
	return ((CSR_READ_4(sc, GMIIControl) & GMI_DATA) >> GMI_DATA_SHIFT);
}

static void
sis_miibus_writereg(self, phy, reg, data)
	struct device		*self;
	int			phy, reg, data;
{
	struct sis_softc	*sc = (struct sis_softc *)self;
	miibus_cmd(sc, (((uint32_t) data) << GMI_DATA_SHIFT) |
		(((uint32_t) phy) << GMI_PHY_SHIFT) |
		(reg << GMI_REG_SHIFT) | GMI_OP_WR | GMI_REQ);
}

static void
sis_miibus_statchg(self)
	struct device		*self;
{
	struct sis_softc	*sc = (struct sis_softc *)self;

	sis_init(sc->sis_ifp);
}

static u_int32_t
sis_mchash(sc, addr)
	struct sis_softc	*sc;
	const uint8_t		*addr;
{
	return (ether_crc32_be(addr, ETHER_ADDR_LEN) >> 26);
}

static void
sis_setmulti(sc)
	struct sis_softc	*sc;
{
	struct ifnet		*ifp = sc->sis_ifp;
	struct arpcom		*ac = &sc->arpcom;
	struct ether_multi	*enm;
	struct ether_multistep	step;
	uint32_t		hashes[2] = { 0, 0 }, rxfilt;
	//struct ifmultiaddr	*ifma;

	rxfilt = AcceptMyPhys | 0x0052;

	if (ifp->if_flags & IFF_PROMISC) {
		rxfilt |= AcceptAllPhys;
	} else {
		rxfilt &= ~AcceptAllPhys;
	}

	if (ifp->if_flags & IFF_BROADCAST) {
		rxfilt |= AcceptBroadcast;
	} else {
		rxfilt &= ~AcceptBroadcast;
	}

	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
allmulti:
		rxfilt |= AcceptMulticast;
		CSR_WRITE_2(sc, RxMacControl, rxfilt);
		CSR_WRITE_4(sc, RxHashTable, 0xFFFFFFFF);
		CSR_WRITE_4(sc, RxHashTable2, 0xFFFFFFFF);
		return;
	}

	/* now program new ones */
	//TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
	ETHER_FIRST_MULTI(step, ac, enm);
	while (enm != NULL) {
		if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
			ifp->if_flags |= IFF_ALLMULTI;
			goto allmulti;
		}

		int bit_nr = sis_mchash(sc, LLADDR((struct sockaddr_dl *)
					    enm->enm_addrlo));
		hashes[bit_nr >> 5] |= 1 << (bit_nr & 31);
		rxfilt |= AcceptMulticast;
		ETHER_NEXT_MULTI(step, enm);
	}

	CSR_WRITE_2(sc, RxMacControl, rxfilt);
	CSR_WRITE_4(sc, RxHashTable, hashes[0]);
	CSR_WRITE_4(sc, RxHashTable2, hashes[1]);
}

static void
sis_reset(sc)
	struct sis_softc	*sc;
{
	CSR_WRITE_4(sc, IntrMask, 0);
	CSR_WRITE_4(sc, IntrStatus, 0xffffffff);

	CSR_WRITE_4(sc, TxControl, 0x00001a00);
	CSR_WRITE_4(sc, RxControl, 0x00001a1d);

	CSR_WRITE_4(sc, IntrControl, 0x8000);
	SIS_PCI_COMMIT();
	DELAY(100);
	CSR_WRITE_4(sc, IntrControl, 0x0);

	CSR_WRITE_4(sc, IntrMask, 0);
	CSR_WRITE_4(sc, IntrStatus, 0xffffffff);
}

/*
 * Probe for an SiS chip. Check the PCI vendor and device
 * IDs against our list and return a device name if we find a match.
 */
static int
sis_probe(parent, match, aux)
    	struct device		*parent;
	void			*match;
	void			*aux;
{
	return (pci_matchbyid((struct pci_attach_args *)aux, sis_devices,
	    sizeof(sis_devices)/sizeof(sis_devices[0])));
}

/*
 * Attach the interface. Allocate softc structures, do ifmedia
 * setup and ethernet/BPF attach.
 */
static void
sis_attach(parent, self, aux)
	struct device		*parent;
	struct device		*self;
	void			*aux;
{
	const char		*intrstr = NULL;
	struct sis_softc	*sc = (struct sis_softc *)self;
	struct pci_attach_args	*pa = aux;
	pci_chipset_tag_t	pc = pa->pa_pc;
	pci_intr_handle_t	ih;
	bus_size_t		size;
	struct ifnet		*ifp;
	bus_dma_segment_t	seg;
	int			nseg;
	struct sis19x_list_data	*ld;
	struct sis19x_chain_data *cd;
	int			i, error = 0;

	ld = &sc->sis_ldata;
	cd = &sc->sis_cdata;

#if 0
	if (PCI_PRODUCT(pa->pa_id) != SIS_TYPE_190) {
		printf("SiS191 is not supportet yet :-(\n");
		error = ENXIO;
		goto fail;
	}
#endif

	sc->sis_type = SIS_TYPE_190;
	sc->sis_rev = PCI_REVISION(pa->pa_class); /* XXX unused */

	/* TODO: What about power management ? */

	/*
	 * Handle power management nonsense.
	 */
#if 0
/* power management registers */
#define SIS_PCI_CAPID		0x50 /* 8 bits */
#define SIS_PCI_NEXTPTR		0x51 /* 8 bits */
#define SIS_PCI_PWRMGMTCAP	0x52 /* 16 bits */
#define SIS_PCI_PWRMGMTCTRL	0x54 /* 16 bits */

#define SIS_PSTATE_MASK		0x0003
#define SIS_PSTATE_D0		0x0000
#define SIS_PSTATE_D1		0x0001
#define SIS_PSTATE_D2		0x0002
#define SIS_PSTATE_D3		0x0003
#define SIS_PME_EN		0x0010
#define SIS_PME_STATUS		0x8000
#define SIS_PCI_INTLINE		0x3C
#define SIS_PCI_LOMEM		0x14
	command = pci_conf_read(pc, pa->pa_tag, SIS_PCI_CAPID) & 0x000000FF;
	if (command == 0x01) {

		command = pci_conf_read(pc, pa->pa_tag, SIS_PCI_PWRMGMTCTRL);
		if (command & SIS_PSTATE_MASK) {
			u_int32_t		iobase, membase, irq;

			/* Save important PCI config data. */
			iobase = pci_conf_read(pc, pa->pa_tag, SIS_PCI_LOIO);
			membase = pci_conf_read(pc, pa->pa_tag, SIS_PCI_LOMEM);
			irq = pci_conf_read(pc, pa->pa_tag, SIS_PCI_INTLINE);

			/* Reset the power state. */
			printf("%s: chip is in D%d power mode -- setting to D0\n",
			    sc->sc_dev.dv_xname, command & SIS_PSTATE_MASK);
			command &= 0xFFFFFFFC;
			pci_conf_write(pc, pa->pa_tag, SIS_PCI_PWRMGMTCTRL, command);

			/* Restore PCI config data. */
			pci_conf_write(pc, pa->pa_tag, SIS_PCI_LOIO, iobase);
			pci_conf_write(pc, pa->pa_tag, SIS_PCI_LOMEM, membase);
			pci_conf_write(pc, pa->pa_tag, SIS_PCI_INTLINE, irq);
		}
	}
#endif

	/*
	 * Map control/status registers.
	 */
	//pci_enable_busmaster(dev);

	/* Map IO */
	if ((error = pci_mapreg_map(pa, SIS_PCI_LOMEM, PCI_MAPREG_TYPE_MEM, 0,
	    &sc->sis_btag, &sc->sis_bhandle, NULL, &size, 0))) {
		printf(": can't map i/o space (code %d)\n", error);
		return;
 	}

	/* Allocate interrupt */
	if (pci_intr_map(pa, &ih)) {
		printf(": couldn't map interrupt\n");
		goto intfail;
	}
	intrstr = pci_intr_string(pc, ih);
	sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, sis_intr, sc,
	    self->dv_xname);
	if (sc->sc_ih == NULL) {
		printf(": couldn't establish interrupt");
		if (intrstr != NULL)
			printf(" at %s", intrstr);
		printf("\n");
		goto intfail;
	}

	/* Reset the adapter. */
	sis_reset(sc);

	/*
	 * Get MAC address from the EEPROM.
	 */
 	sis_get_mac_addr_eeprom(sc, (caddr_t)&sc->arpcom.ac_enaddr);

	printf(": %s, address %s\n", intrstr,
	    ether_sprintf(sc->arpcom.ac_enaddr));

	/*
	 * Now do all the DMA mapping stuff
	 */

	sc->sis_tag = pa->pa_dmat;

	/* First create TX/RX busdma maps. */
	for (i = 0; i < SIS_RX_RING_CNT; i++) {
		error = bus_dmamap_create(sc->sis_tag, MCLBYTES, 1, MCLBYTES,
			0, BUS_DMA_NOWAIT, &cd->sis_rx_map[i]);
		if (error) {
			printf("cannot init the RX map array!\n");
			goto fail;
		}
	}

	for (i = 0; i < SIS_TX_RING_CNT; i++) {
		error = bus_dmamap_create(sc->sis_tag, MCLBYTES, 1, MCLBYTES,
			0, BUS_DMA_NOWAIT, &cd->sis_tx_map[i]);
		if (error) {
			printf("cannot init the TX map array!\n");
			goto fail;
		}
	}


	/*
	 * Now allocate a tag for the DMA descriptor lists and a chunk
	 * of DMA-able memory based on the tag.  Also obtain the physical
	 * addresses of the RX and TX ring, which we'll need later.
	 * All of our lists are allocated as a contiguous block
	 * of memory.
	 */

	/* RX */

	error = bus_dmamem_alloc(sc->sis_tag, SIS_RX_RING_SZ, PAGE_SIZE, 0,
		&seg, 1, &nseg, BUS_DMA_NOWAIT);
	if (error) {
		printf("no memory for rx list buffers!\n");
		goto fail;
	}

	error = bus_dmamem_map(sc->sis_tag, &seg, nseg, SIS_RX_RING_SZ,
		(caddr_t *)&ld->sis_rx_ring, BUS_DMA_NOWAIT);
	if (error) {
		printf("can't map rx list buffers!\n");
		goto fail;
	}

	error = bus_dmamap_create(sc->sis_tag, SIS_RX_RING_SZ, 1,
		SIS_RX_RING_SZ, 0, BUS_DMA_NOWAIT, &ld->sis_rx_dmamap);
	if (error) {
		printf("can't alloc rx list map!\n");
		goto fail;
	}

	error = bus_dmamap_load(sc->sis_tag, ld->sis_rx_dmamap,
		(caddr_t)ld->sis_rx_ring, SIS_RX_RING_SZ,
		NULL, BUS_DMA_NOWAIT);
	if (error) {
		printf("cannot load rx ring mapping!\n");
		bus_dmamem_unmap(sc->sis_tag,
			(caddr_t)ld->sis_rx_ring, SIS_RX_RING_SZ);
		bus_dmamap_destroy(sc->sis_tag, ld->sis_rx_dmamap);
		bus_dmamem_free(sc->sis_tag, &seg, nseg);
		goto fail;
	}

	/* TX */

	error = bus_dmamem_alloc(sc->sis_tag, SIS_TX_RING_SZ, PAGE_SIZE, 0,
		&seg, 1, &nseg, BUS_DMA_NOWAIT);
	if (error) {
		printf("no memory for tx list buffers!\n");
		goto fail;
	}

	error = bus_dmamem_map(sc->sis_tag, &seg, nseg, SIS_TX_RING_SZ,
		(caddr_t *)&ld->sis_tx_ring, BUS_DMA_NOWAIT);
	if (error) {
		printf("can't map tx list buffers!\n");
		goto fail;
	}

	error = bus_dmamap_create(sc->sis_tag, SIS_TX_RING_SZ, 1,
		SIS_TX_RING_SZ, 0, BUS_DMA_NOWAIT, &ld->sis_tx_dmamap);
	if (error) {
		printf("can't alloc tx list map!\n");
		goto fail;
	}

	error = bus_dmamap_load(sc->sis_tag, ld->sis_tx_dmamap,
		(caddr_t)ld->sis_tx_ring, SIS_TX_RING_SZ,
		NULL, BUS_DMA_NOWAIT);
	if (error) {
		printf("cannot load tx ring mapping!\n");
		bus_dmamem_unmap(sc->sis_tag,
			(caddr_t)ld->sis_tx_ring, SIS_TX_RING_SZ);
		bus_dmamap_destroy(sc->sis_tag, ld->sis_tx_dmamap);
		bus_dmamem_free(sc->sis_tag, &seg, nseg);
		goto fail;
	}

	timeout_set(&sc->sis_timeout, sis_tick, sc);

	sc->sis_ifp = ifp = &sc->arpcom.ac_if;
	ifp->if_softc = sc;
	/* do I really need to set this? if_sis.c doesn't */
	ifp->if_mtu = ETHERMTU;
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	/*ifp->if_capenable = ifp->if_capabilities; XXX only in fBSD */
	ifp->if_ioctl = sis_ioctl;
	ifp->if_start = sis_start;
	ifp->if_watchdog = sis_watchdog;
	ifp->if_baudrate = 10000000; /* XXX 10MBit ?!? */
	ifp->if_init = sis_init; /* XXX not in if_sis.c */
	IFQ_SET_MAXLEN(&ifp->if_snd, SIS_TX_RING_CNT - 1);
	IFQ_SET_READY(&ifp->if_snd);
	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);

	/*
	 * Do MII setup.
	 */

	sc->sc_mii.mii_ifp = ifp;
	sc->sc_mii.mii_readreg = sis_miibus_readreg;
	sc->sc_mii.mii_writereg = sis_miibus_writereg;
	sc->sc_mii.mii_statchg = sis_miibus_statchg;
	ifmedia_init(&sc->sc_mii.mii_media, 0,
			sis_ifmedia_upd,sis_ifmedia_sts);
	/* TODO: This won't work. mii_phy_probe is of type void. */
	mii_phy_probe(self, &sc->sc_mii, 0xffffffff);

	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
		ifmedia_add(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_NONE);
	} else
		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);

	/*
	 * Call MI attach routine.
	 */
	if_attach(ifp);
	ether_ifattach(ifp);

	shutdownhook_establish(sis_shutdown, sc);

fail:
	pci_intr_disestablish(pc, sc->sc_ih);

intfail:
	bus_space_unmap(sc->sis_btag, sc->sis_bhandle, size);
}

/*
 * Stop all chip I/O so that the kernel's probe routines don't
 * get confused by errant DMAs when rebooting.
 */
static void
sis_shutdown(v)
	void			*v;
{
	struct sis_softc	*sc = (struct sis_softc *)v;

	sis_reset(sc);
	sis_stop(sc);
}



/*
 * Initialize the TX descriptors.
 */
static int
sis_list_tx_init(sc)
	struct sis_softc	*sc;
{
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;

	bzero(ld->sis_tx_ring, SIS_TX_RING_SZ);
	ld->sis_tx_ring[SIS_TX_RING_CNT - 1].sis_flags |= RING_END;
	cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0;

	return (0);
}

static int
sis_list_tx_free(sc)
	struct sis_softc	*sc;
{
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	register int		i;

	for (i = 0; i < SIS_TX_RING_CNT; i++) {
		if (cd->sis_tx_mbuf[i] != NULL) {
			bus_dmamap_unload(sc->sis_tag, cd->sis_tx_map[i]);
			m_free(cd->sis_tx_mbuf[i]);
			cd->sis_tx_mbuf[i] = NULL;
		}
	}

	return (0);
}

/*
 * Initialize the RX descriptors and allocate mbufs for them. Note that
 * we arrange the descriptors in a closed ring, so that the last descriptor
 * has RING_END flag set.
 */
static int
sis_list_rx_init(sc)
	struct sis_softc	*sc;
{
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	register int		i;

	bzero(ld->sis_rx_ring, SIS_RX_RING_SZ);
	for (i = 0; i < SIS_RX_RING_CNT; i++) {
		if (sis_newbuf(sc, i, NULL) == ENOBUFS) {
			SIS_OUT("unable to allocate MBUFs, %d", i);
			return (ENOBUFS);
		}
	}

	ld->sis_rx_ring[SIS_RX_RING_CNT - 1].sis_flags |= RING_END;
	cd->sis_rx_prod = 0;

	return (0);
}

static int
sis_list_rx_free(sc)
	struct sis_softc	*sc;
{
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	register int		i;

	for (i = 0; i < SIS_RX_RING_CNT; i++) {
		if (cd->sis_rx_mbuf[i] != NULL) {
			bus_dmamap_unload(sc->sis_tag, cd->sis_rx_map[i]);
			m_free(cd->sis_rx_mbuf[i]);
			cd->sis_rx_mbuf[i] = NULL;
		}
	}

	return (0);
}

/*
 * Initialize an RX descriptor and attach an MBUF cluster.
 */
static int
sis_newbuf(sc, i, m)
	struct sis_softc	*sc;
	u_int32_t		i;
	struct mbuf		*m;
{
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	int			error, alloc;

	if (m == NULL) {
		m = m_gethdr(M_DONTWAIT, MT_DATA);
		if (m == NULL) {
			SIS_OUT("unable to get new MBUF");
			return (ENOBUFS);
		}
		cd->sis_rx_mbuf[i] = m;
		alloc = 1;
	} else {
		m->m_data = m->m_ext.ext_buf;
		alloc = 0;
	}

	m->m_len = m->m_pkthdr.len = MCLBYTES;

	if (alloc) {
		error = bus_dmamap_load_mbuf(sc->sis_tag, cd->sis_rx_map[i],
				m, BUS_DMA_NOWAIT);
		if (error) {
			SIS_OUT("unable to map and load the MBUF");
			m_freem(m);
			return (ENOBUFS);
		}
	}

	/* This is used both to initialize the newly created RX
	 * descriptor as well as for re-initializing it for reuse. */
	ld->sis_rx_ring[i].sis_sts_size = 0;
	ld->sis_rx_ring[i].sis_cmdsts =
		htole32(OWNbit | INTbit | IPbit | TCPbit | UDPbit);
	ld->sis_rx_ring[i].sis_ptr =
		htole32(cd->sis_rx_map[i]->dm_segs[0].ds_addr);
	ld->sis_rx_ring[i].sis_flags =
		htole32(cd->sis_rx_map[i]->dm_segs[0].ds_len);
	KASSERT(cd->sis_rx_map[i]->dm_nsegs == 1,
			("wrong number of segments, should be 1"));

	bus_dmamap_sync(sc->sis_tag, cd->sis_rx_map[i], 0,
			cd->sis_rx_map[i]->dm_mapsize,
			BUS_DMASYNC_PREREAD);

	return (0);
}

/*
 * A frame has been uploaded: pass the resulting mbuf chain up to
 * the higher level protocols.
 */
static void
sis_rxeof(sc)
	struct sis_softc	*sc;
{
        struct mbuf		*m, *m0;
        struct ifnet		*ifp = sc->sis_ifp;
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	struct sis19x_desc	*cur_rx;
	u_int32_t		i, rxstat, total_len = 0;

	for (i = cd->sis_rx_prod; !SIS_OWNDESC(&ld->sis_rx_ring[i]);
	    SIS_INC(i, SIS_RX_RING_CNT)) {
		bus_dmamap_sync(sc->sis_tag, cd->sis_rx_map[i], 0,
				cd->sis_rx_map[i]->dm_mapsize,
				BUS_DMASYNC_POSTREAD);

		cur_rx = &ld->sis_rx_ring[i];
		rxstat = SIS_RXSTATUS(cur_rx);
		total_len = SIS_RXSIZE(cur_rx);
		m = cd->sis_rx_mbuf[i];

		/*
		 * If an error occurs, update stats, clear the
		 * status word and leave the mbuf cluster in place:
		 * it should simply get re-used next time this descriptor
	 	 * comes up in the ring.
		 */
		if (rxstat & RX_ERR_BITS) {
			SIS_OUT("error_bits=%#x", rxstat);
			ifp->if_ierrors++;
			/* TODO: better error differentiation */
			sis_newbuf(sc, i, m);
			continue;
		}

		/* No errors; receive the packet. */
#if 0 //def __NO_STRICT_ALIGNMENT
		if (sis_newbuf(sc, i, NULL) == 0) {
			m->m_pkthdr.len = m->m_len = total_len;
		} else
#endif
		{
			m0 = m_devget(mtod(m, char *), total_len,
			    ETHER_ALIGN, ifp, NULL);
			sis_newbuf(sc, i, m);
			if (m0 == NULL) {
				SIS_OUT("unable to copy MBUF");
				ifp->if_ierrors++;
				continue;
			}
			m = m0;
		}

		ifp->if_ipackets++;
		m->m_pkthdr.rcvif = ifp;

#if NBPFILTER > 0
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif

		/* pass it on. */
		ether_input_mbuf(ifp, m);
	}

	cd->sis_rx_prod = i;
}


/*
 * A frame was downloaded to the chip. It's safe for us to clean up
 * the list buffers.
 */

static void
sis_txeof(sc)
	struct sis_softc	*sc;
{
	struct ifnet		*ifp = sc->sis_ifp;
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	struct sis19x_desc	*cur_tx;
	u_int32_t		i, txstat;

	/*
	 * Go through our tx list and free mbufs for those
	 * frames that have been transmitted.
	 */
	for (i = cd->sis_tx_cons; cd->sis_tx_cnt > 0 &&
	       !SIS_OWNDESC(&ld->sis_tx_ring[i]);
	    cd->sis_tx_cnt--, SIS_INC(i, SIS_TX_RING_CNT)) {
		cur_tx = &ld->sis_tx_ring[i];
		txstat = letoh32(cur_tx->sis_cmdsts);
		bus_dmamap_sync(sc->sis_tag, cd->sis_tx_map[i], 0,
				cd->sis_tx_map[i]->dm_mapsize,
				BUS_DMASYNC_POSTWRITE);

		/* current slot is transferred now */

		if (txstat & TX_ERR_BITS) {
			SIS_OUT("error_bits=%#x", txstat);
			ifp->if_oerrors++;
			/* TODO: better error differentiation */
		}

		ifp->if_opackets++;
		if (cd->sis_tx_mbuf[i] != NULL) {
			bus_dmamap_unload(sc->sis_tag, cd->sis_tx_map[i]);
			m_free(cd->sis_tx_mbuf[i]);
			cd->sis_tx_mbuf[i] = NULL;
		}
		cur_tx->sis_sts_size = 0;
		cur_tx->sis_cmdsts = 0;
		cur_tx->sis_ptr = 0;
		cur_tx->sis_flags &= RING_END;
	}

	if (i != cd->sis_tx_cons) {
		/* we freed up some buffers */
		cd->sis_tx_cons = i;
		ifp->if_flags &= ~IFF_OACTIVE;
	}

	sc->sis_watchdog_timer = (cd->sis_tx_cnt == 0) ? 0 : 5;
}

static void
sis_tick(xsc)
	void			*xsc;
{
	struct sis_softc	*sc = xsc;
	struct mii_data		*mii;
	struct ifnet		*ifp = sc->sis_ifp;

	sc->in_tick = 1;

	mii = &sc->sc_mii;
	mii_tick(mii);

	sis_watchdog(ifp);

	if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE &&
	    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
		sc->sis_link++;
		if (!IFQ_IS_EMPTY(&ifp->if_snd))
			sis_start(ifp);
	}

	timeout_add_sec(&sc->sis_timeout, 1);

	sc->in_tick = 0;
}

static int
sis_intr(arg)
	void			*arg;
{
	struct sis_softc	*sc = arg;
	struct ifnet		*ifp = sc->sis_ifp;
	int			status;
	int			claimed = 0;

	if (sc->sis_stopped)	/* Most likely shared interrupt */
		return (claimed);

	DISABLE_INTERRUPTS(sc);

	for (;;) {
		/* Reading the ISR register clears all interrupts. */
		status = CSR_READ_4(sc, IntrStatus);
		if ((status == 0xffffffff) || (status == 0x0))
			break;

		claimed = 1; /* XXX just a guess to put this here */

		CSR_WRITE_4(sc, IntrStatus, status);

		if (status & TxQInt)
			sis_txeof(sc);

		if (status & RxQInt)
			sis_rxeof(sc);
	}

	ENABLE_INTERRUPTS(sc);

	if (!IFQ_IS_EMPTY(&ifp->if_snd))
		sis_start(ifp);

	return (claimed);
}

/*
 * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
 * pointers to the fragment pointers.
 */
static int
sis_encap(sc, m_head, txidx)
	struct sis_softc	*sc;
	struct mbuf		*m_head;
	u_int32_t		*txidx;
{
	struct mbuf		*m;
	struct sis19x_list_data	*ld = &sc->sis_ldata;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	int			error, i, cnt = 0;

	/*
	 * If there's no way we can send any packets, return now.
	 */
	if (SIS_TX_RING_CNT - cd->sis_tx_cnt < 2)
		return (ENOBUFS);

	if(m_defrag(m_head, M_DONTWAIT)) {
		SIS_OUT("unable to defragment MBUFs");
		return (ENOBUFS);
	}
	
	/*
 	 * Start packing the mbufs in this chain into
	 * the fragment pointers. Stop when we run out
 	 * of fragments or hit the end of the mbuf chain.
	 */
	i = *txidx;

	for (m = m_head; m != NULL; m = m->m_next) {
		if (m->m_len == 0)
			continue;
		if ((SIS_TX_RING_CNT - (cd->sis_tx_cnt + cnt)) < 2)
			return (ENOBUFS);
		cd->sis_tx_mbuf[i] = m;
		error = bus_dmamap_load_mbuf(sc->sis_tag, cd->sis_tx_map[i],
				m, BUS_DMA_NOWAIT);
		if (error) {
			SIS_OUT("unable to load the MBUF");
			return (ENOBUFS);
		}

		ld->sis_tx_ring[i].sis_sts_size =
			htole32(cd->sis_tx_map[i]->dm_segs->ds_len);
		ld->sis_tx_ring[i].sis_cmdsts =
			htole32(OWNbit | INTbit | PADbit | CRCbit | DEFbit);
		ld->sis_tx_ring[i].sis_ptr =
			htole32(cd->sis_tx_map[i]->dm_segs->ds_addr);
		ld->sis_tx_ring[i].sis_flags |=
			htole32(cd->sis_tx_map[i]->dm_segs->ds_len);
		KASSERT(cd->sis_tx_map[i]->dm_nsegs == 1,
				("wrong number of segments, should be 1"));

		bus_dmamap_sync(sc->sis_tag, cd->sis_tx_map[i], 0,
				cd->sis_tx_map[i]->dm_mapsize,
				BUS_DMASYNC_PREWRITE);
		SIS_INC(i, SIS_TX_RING_CNT);
		cnt++;
	}

	if (m != NULL) {
		SIS_OUT("unable to encap all MBUFs");
		return (ENOBUFS);
	}

	cd->sis_tx_cnt += cnt;
	*txidx = i;

	return (0);
}

/*
 * Main transmit routine. To avoid having to do mbuf copies, we put pointers
 * to the mbuf data regions directly in the transmit lists. We also save a
 * copy of the pointers since the transmit list fragment pointers are
 * physical addresses.
 */
static void
sis_start(ifp)
	struct ifnet		*ifp;
{
	struct sis_softc	*sc = ifp->if_softc;
	struct mbuf		*m_head = NULL;
	struct sis19x_chain_data	*cd = &sc->sis_cdata;
	u_int32_t		i, queued = 0;

	if (!sc->sis_link) {
		return;
	}

	if (ifp->if_flags & IFF_OACTIVE) {
		return;
	}

	i = cd->sis_tx_prod;

	while (cd->sis_tx_mbuf[i] == NULL) {
		IFQ_POLL(&ifp->if_snd, m_head);
		if (m_head == NULL)
			break;

		if (sis_encap(sc, m_head, &i)) {
			ifp->if_flags |= IFF_OACTIVE;
			break;
		}

		/* now we are committed to transmit the packet */
		IFQ_DEQUEUE(&ifp->if_snd, m_head);
		queued++;

		/*
		 * If there's a BPF listener, bounce a copy of this frame
		 * to him.
		 */
#if NBPFILTER > 0
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT);
#endif
	}

	if (queued) {
		/* Transmit */
		cd->sis_tx_prod = i;
		SIS_SETBIT(sc, TxControl, CmdReset);

		/*
		 * Set a timeout in case the chip goes out to lunch.
		 */
		sc->sis_watchdog_timer = 5;
	}
}

/* TODO: Find out right return codes */
static int
sis_init(ifp)
	struct ifnet		*ifp;
{
	struct sis_softc	*sc = ifp->if_softc;

	/*
	 * Cancel pending I/O and free all RX/TX buffers.
	 */
	sis_stop(sc);
	sc->sis_stopped = 0;

	/* Init circular RX list. */
	if (sis_list_rx_init(sc) == ENOBUFS) {
		printf("initialization failed: no "
			"memory for rx buffers\n");
		sis_stop(sc);
		return 1;
	}

	/* Init TX descriptors. */
	sis_list_tx_init(sc);

	sis_reset(sc);

	/*
	 * Load the address of the RX and TX lists.
	 */
	CSR_WRITE_4(sc, TxDescStartAddr,
			sc->sis_ldata.sis_tx_dmamap->dm_segs[0].ds_addr);
	CSR_WRITE_4(sc, RxDescStartAddr,
			sc->sis_ldata.sis_rx_dmamap->dm_segs[0].ds_addr);

/* 	CSR_WRITE_4(sc, PMControl, 0xffc00000); */
	CSR_WRITE_4(sc, Reserved2, 0);

	CSR_WRITE_4(sc, IntrStatus, 0xffffffff);
	DISABLE_INTERRUPTS(sc);


	/*
	 * Default is 100Mbps.
	 * A bit strange: 100Mbps is 0x1801 elsewhere -- FR 2005/06/09
	 */
	CSR_WRITE_4(sc, StationControl, 0x04001801); // 1901

	CSR_WRITE_4(sc, GMacIOCR, 0x0);
	CSR_WRITE_4(sc, GMacIOCTL, 0x0);

	CSR_WRITE_4(sc, TxMacControl, 0x2364);		/* 0x60 */
	CSR_WRITE_4(sc, TxMacTimeLimit, 0x000f);
	CSR_WRITE_4(sc, RGMIIDelay, 0x0);
	CSR_WRITE_4(sc, Reserved3, 0x0);

	CSR_WRITE_4(sc, RxWakeOnLan, 0x80ff0000);
	CSR_WRITE_4(sc, RxWakeOnLanData, 0x80ff0000);
	CSR_WRITE_4(sc, RxMPSControl, 0x0);
	CSR_WRITE_4(sc, Reserved4, 0x0);

	SIS_PCI_COMMIT();

	/*
	 * Load the multicast filter.
	 */
	sis_setmulti(sc);

	/*
	 * Enable interrupts.
	 */
	ENABLE_INTERRUPTS(sc);

	/* Enable receiver and transmitter. */
	SIS_SETBIT(sc, TxControl, CmdTxEnb | CmdReset);
	SIS_SETBIT(sc, RxControl, CmdRxEnb | CmdReset);

	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	if (!sc->in_tick)
		timeout_add_sec(&sc->sis_timeout, 1);

	return 0;
}

/*
 * Set media options.
 */
static int
sis_ifmedia_upd(ifp)
	struct ifnet		*ifp;
{
	struct sis_softc	*sc = ifp->if_softc;
	struct mii_data		*mii;

	mii = &sc->sc_mii;
	sc->sis_link = 0;
	if (mii->mii_instance) {
		struct mii_softc	*miisc;
		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
			mii_phy_reset(miisc);
	}
	mii_mediachg(mii);

	return (0);
}

/*
 * Report current media status.
 */
static void
sis_ifmedia_sts(ifp, ifmr)
	struct ifnet		*ifp;
	struct ifmediareq	*ifmr;
{
	struct sis_softc	*sc = ifp->if_softc;
	struct mii_data		*mii;

	mii = &sc->sc_mii;
	mii_pollstat(mii);
	ifmr->ifm_active = mii->mii_media_active;
	ifmr->ifm_status = mii->mii_media_status;
}

static int
sis_ioctl(ifp, command, data)
	struct ifnet		*ifp;
	u_long			command;
	caddr_t			data;
{
	struct sis_softc	*sc = ifp->if_softc;
	struct ifreq		*ifr = (struct ifreq *) data;
	struct mii_data		*mii;
	int			error = 0;

	switch(command) {
	case SIOCSIFFLAGS:
		if (ifp->if_flags & IFF_UP)
			sis_init(ifp);
		else if (ifp->if_flags & IFF_RUNNING)
			sis_stop(sc);
		error = 0;
		break;
	case SIOCADDMULTI:
	case SIOCDELMULTI:
		sis_setmulti(sc);
		error = 0;
		break;
	case SIOCGIFMEDIA:
	case SIOCSIFMEDIA:
		mii = &sc->sc_mii;
		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
		break;
	default:
		error = ether_ioctl(ifp, &sc->arpcom, command, data);
		break;
	}

	return (error);
}

static void
sis_watchdog(ifp)
	struct ifnet		*ifp;
{
	struct sis_softc	*sc = ifp->if_softc;

	if (sc->sis_stopped)
		return;

	if (sc->sis_watchdog_timer == 0 || --sc->sis_watchdog_timer >0)
		return;

	SIS_OUT("watchdog timeout");
	sc->sis_ifp->if_oerrors++;

	sis_stop(sc);
	sis_reset(sc);
	sis_init(ifp);

	if (!IFQ_IS_EMPTY(&sc->sis_ifp->if_snd))
		sis_start(sc->sis_ifp);
}

/*
 * Stop the adapter and free any mbufs allocated to the
 * RX and TX lists.
 */
static void
sis_stop(sc)
	struct sis_softc	*sc;
{
	struct ifnet		*ifp = sc->sis_ifp;

	if (sc->sis_stopped)
		return;

	sc->sis_watchdog_timer = 0;

	timeout_del(&sc->sis_timeout);

	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);

	DISABLE_INTERRUPTS(sc);

	CSR_WRITE_4(sc, IntrControl, 0x8000);
	SIS_PCI_COMMIT();
	DELAY(100);
	CSR_WRITE_4(sc, IntrControl, 0x0);

	SIS_CLRBIT(sc, TxControl, CmdTxEnb);
	SIS_CLRBIT(sc, RxControl, CmdRxEnb);
	DELAY(100);
	CSR_WRITE_4(sc, TxDescStartAddr, 0);
	CSR_WRITE_4(sc, RxDescStartAddr, 0);

	sc->sis_link = 0;

	/*
	 * Free data in the RX lists.
	 */
	sis_list_rx_free(sc);

	/*
	 * Free the TX list buffers.
	 */
	sis_list_tx_free(sc);

	sc->sis_stopped = 1;
}
/*-
 * Copyright (c) 2007, 2008 Alexander Pohoyda <alexander.poho...@gmx.net>
 * Copyright (c) 1997, 1998, 1999
 *	Bill Paul <wp...@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL AUTHORS OR
 * THE VOICES IN THEIR HEADS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD$
 */

struct sis19x_desc {
	volatile u_int32_t	sis_sts_size;
	volatile u_int32_t	sis_cmdsts;
	volatile u_int32_t	sis_ptr;
	volatile u_int32_t	sis_flags;
};

#define SIS_RX_RING_CNT		256 /* [8, 1024] */
#define SIS_TX_RING_CNT		256 /* [8, 8192] */

#define SIS_RX_RING_SZ		(SIS_RX_RING_CNT * sizeof(struct sis19x_desc))
#define SIS_TX_RING_SZ		(SIS_TX_RING_CNT * sizeof(struct sis19x_desc))

struct sis19x_list_data {
	struct sis19x_desc	*sis_rx_ring;
	struct sis19x_desc	*sis_tx_ring;
	bus_dmamap_t		sis_rx_dmamap;
	bus_dmamap_t		sis_tx_dmamap;
};

struct sis19x_chain_data {
	int			sis_rx_prod;
	int			sis_tx_prod;
	int			sis_tx_cons;
	int			sis_tx_cnt;
	struct mbuf		*sis_rx_mbuf[SIS_RX_RING_CNT];
	struct mbuf		*sis_tx_mbuf[SIS_TX_RING_CNT];
	bus_dmamap_t		sis_rx_map[SIS_RX_RING_CNT];
	bus_dmamap_t		sis_tx_map[SIS_TX_RING_CNT];
};


/*
 * SiS PCI vendor ID.
 */
#define SIS_VENDORID		0x1039

/*
 * SiS PCI device IDs
 */
#define SIS_DEVICEID_190	0x0190
#define SIS_DEVICEID_191	0x0191

struct sis_type {
	u_int16_t		sis_vid;
	u_int16_t		sis_did;
	char			*sis_name;
};

#define SIS_TYPE_190	5

struct sis_softc {
    	struct device		sc_dev;
	void			*sc_ih;
	struct ifnet		*sis_ifp;	/* interface info */
	struct resource		*sis_res[2];
	void			*sis_intrhand;
	mii_data_t		sc_mii;
	struct arpcom		arpcom;
	bus_space_handle_t	sis_bhandle;
	bus_space_tag_t		sis_btag;
	u_int8_t		sis_type;
	u_int8_t		sis_rev;
	u_int8_t		sis_link;
	struct timeout		sis_timeout;
	bus_dma_tag_t		sis_tag;
	struct sis19x_list_data	sis_ldata;
	struct sis19x_chain_data sis_cdata;
	int			sis_watchdog_timer;
	int			sis_stopped;
	int			in_tick;
};

#define SIS_TIMEOUT		1000
#define ETHER_ALIGN		2

Attachment: signature.asc
Description: PGP signature

Reply via email to