Some fixes, some warning fixed, added a lot of code for the MII handling (almost completely "stolen" from Axel's sis900 driver), not yet working.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6988 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Stefano Ceccherini
2004-03-15 07:13:03 +00:00
parent 599fbbf010
commit 80127f8edb
5 changed files with 451 additions and 89 deletions
@@ -67,14 +67,13 @@ wb840_open(const char *name, uint32 flags, void** cookie)
LOG(("wb840: reg_base=%x\n", (int)data->reg_base));
wb_read_eeprom(data, &data->myaddr, 0, 3, 0);
print_address((void*)&data->myaddr);
data->wb_cachesize = gPci->read_pci_config(data->pciInfo->bus, data->pciInfo->device,
data->pciInfo->function, PCI_line_size, sizeof (PCI_line_size)) & 0xff;
if (wb_create_semaphores(data) == B_OK) {
/* reset the card */
wb_reset(data);
wb_initPHYs(data);
wb_init(data);
/* Setup interrupts */
data->irq = data->pciInfo->u.h0.interrupt_line;
@@ -87,15 +86,9 @@ wb840_open(const char *name, uint32 flags, void** cookie)
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_RX_ON);
write32(data->reg_base + WB_RXSTART, 0xFFFFFFFF);
WB_SETBIT(data->reg_base + WB_NETCFG, WB_NETCFG_TX_ON);
//write32(data->reg_base + WB_TXSTART, 0xFFFFFFFF);
//dump_registers(data);
add_timer(&data->timer, wb_tick, 1000000LL, B_PERIODIC_TIMER);
#if DEBUG
dump_registers(data);
#endif
return B_OK; // Everything after this line is an error
} else
@@ -149,11 +142,13 @@ wb840_read(void* cookie, off_t position, void *buf, size_t* num_bytes)
*num_bytes = 0;
} else {
size = WB_RXBYTES(check);
size -= CRC_SIZE;
LOG((DEVICE_NAME": received %d bytes\n", (int)size));
if (size > WB_MAX_FRAMELEN || size > *num_bytes) {
LOG(("ERROR: Bad frame size: %d", (int)size));
size = *num_bytes;
}
*num_bytes = size;
memcpy(buf, (void *)device->rxBuffer[current], size);
}
@@ -51,43 +51,7 @@
#define MII_DELAY(x) read32(x->reg_base + WB_SIO)
void
wb_eeprom_putbyte(wb_device *device, int addr)
{
int d, i;
int delay;
d = addr | WB_EECMD_READ;
/*
* Feed in each bit and strobe the clock.
*/
for (i = 0x400; i; i >>= 1) {
if (d & i) {
SIO_SET(WB_SIO_EE_DATAIN);
} else {
SIO_CLR(WB_SIO_EE_DATAIN);
}
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
SIO_SET(WB_SIO_EE_CLK);
for (delay = 0; delay < 150; delay++)
MII_DELAY(device);
SIO_CLR(WB_SIO_EE_CLK);
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
}
return;
}
static void
mii_sync(struct wb_device *device)
{
@@ -269,7 +233,7 @@ wb_miibus_readreg(wb_device *device, int phy, int reg)
frame.mii_regaddr = reg;
wb_mii_readreg(device, &frame);
return(frame.mii_data);
return frame.mii_data;
}
@@ -298,6 +262,42 @@ wb_miibus_statchg(wb_device *device)
#define EEPROM_DELAY(x) read32(x->reg_base + WB_SIO)
void
wb_eeprom_putbyte(wb_device *device, int addr)
{
int d, i;
int delay;
d = addr | WB_EECMD_READ;
/*
* Feed in each bit and strobe the clock.
*/
for (i = 0x400; i; i >>= 1) {
if (d & i) {
SIO_SET(WB_SIO_EE_DATAIN);
} else {
SIO_CLR(WB_SIO_EE_DATAIN);
}
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
SIO_SET(WB_SIO_EE_CLK);
for (delay = 0; delay < 150; delay++)
MII_DELAY(device);
SIO_CLR(WB_SIO_EE_CLK);
for (delay = 0; delay < 100; delay++)
MII_DELAY(device);
}
return;
}
static void
wb_eeprom_askdata(wb_device *device, int addr)
@@ -4,6 +4,6 @@
#include "wb840.h"
extern void wb_read_eeprom(struct wb_device *, void* dest, int offset, int count, int swap);
extern uint16 mii_read(struct wb_device *, int phy_id, int location);
extern int wb_miibus_readreg(struct wb_device *, int phy_id, int location);
extern void wb_miibus_writereg(struct wb_device *, int phy, int location, int data);
#endif
+290 -29
View File
@@ -18,6 +18,40 @@
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
// MII chip info table
#define PHY_ID0_WB840_INTERNAL 0x0181
#define PHY_ID1_WB840_INTERNAL 0xb800
#define MII_HOME 0x0001
#define MII_LAN 0x0002
const static struct mii_chip_info
{
const char *name;
uint16 id0, id1;
uint8 types;
}
gMIIChips[] = {
{"WB840 Internal MII PHY", PHY_ID0_WB840_INTERNAL, PHY_ID1_WB840_INTERNAL, MII_LAN},
{NULL,0,0,0}
};
static int
mii_readstatus(wb_device *device)
{
// status bit has to be retrieved 2 times
int i = 0;
int status;
while (i++ < 2)
status = wb_miibus_readreg(device, device->phy, MII_STATUS);
return status;
}
static uint32
physicalAddress(volatile void *addr, uint32 length)
{
@@ -85,35 +119,117 @@ wb_disable_interrupts(struct wb_device *device)
void
wb_reset(wb_device *device)
wb_selectPHY(wb_device *device)
{
int i = 0;
cpu_status status;
uint16 status;
LOG((DEVICE_NAME": reset()\n"));
// ToDo: need to be changed, select PHY in relation to the link mode
device->currentPHY = device->firstPHY;
device->phy = device->currentPHY->address;
status = wb_miibus_readreg(device, device->phy, MII_CONTROL);
status &= ~MII_CONTROL_ISOLATE;
wb_miibus_writereg(device, device->phy, MII_CONTROL, status);
}
uint16
wb_resetPHY(wb_device *device)
{
uint16 status = mii_readstatus(device);
dprintf("wb_resetPHY: %d\n", status);
wb_miibus_writereg(device, device->phy, MII_CONTROL, MII_CONTROL_RESET);
status = disable_interrupts();
return status;
}
status_t
wb_initPHYs(wb_device *device)
{
uint16 phy;
// search for total of 32 possible MII PHY addresses
for (phy = 0; phy < 32; phy++) {
struct mii_phy *mii;
uint16 status;
int i = 0;
device->wb_txthresh = WB_TXTHRESH_INIT;
status = wb_miibus_readreg(device, phy, MII_STATUS);
status = wb_miibus_readreg(device, phy, MII_STATUS);
if (status == 0xffff || status == 0x0000)
// this MII is not accessable
continue;
mii = (struct mii_phy *)malloc(sizeof(struct mii_phy));
if (mii == NULL)
return B_NO_MEMORY;
mii->address = phy;
mii->id0 = wb_miibus_readreg(device, phy, MII_PHY_ID0);
mii->id1 = wb_miibus_readreg(device, phy, MII_PHY_ID1);
mii->types = MII_HOME;
mii->next = device->firstPHY;
device->firstPHY = mii;
write32(device->reg_base + WB_NETCFG, 0L);
write32(device->reg_base + WB_BUSCTL, 0L);
write32(device->reg_base + WB_TXADDR, 0L);
write32(device->reg_base + WB_RXADDR, 0L);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
for (i = 0; i < WB_TIMEOUT; i++) {
if (!(read32(device->reg_base + WB_BUSCTL) & WB_BUSCTL_RESET))
break;
while (gMIIChips[i].name != NULL) {
if (gMIIChips[i].id0 == mii->id0 && gMIIChips[i].id1 == (mii->id1 & 0xfff0)) {
dprintf("Found MII PHY: %s\n", gMIIChips[i].name);
mii->types = gMIIChips[i].types;
break;
}
i++;
}
if (gMIIChips[i].name == NULL)
dprintf("Unknown MII PHY transceiver: id = (%x, %x).\n",mii->id0, mii->id1);
}
if (device->firstPHY == NULL) {
dprintf("No MII PHY transceiver found!\n");
return B_ENTRY_NOT_FOUND;
}
if (i == WB_TIMEOUT)
LOG(("reset hasn't completed!!!"));
wb_selectPHY(device);
// if the internal PHY is selected, reset it
if (device->currentPHY->id0 == PHY_ID0_WB840_INTERNAL
&& (device->currentPHY->id1 & 0xfff0) == PHY_ID1_WB840_INTERNAL) {
if (wb_resetPHY(device) & MII_STATUS_LINK) {
uint16 poll = MII_STATUS_LINK;
while (poll) {
poll ^= wb_miibus_readreg(device, device->phy, MII_STATUS) & poll;
}
}
}
/*
// workaround for ICS1893 PHY
if (info->currentPHY->id0 == PHY_ID0_ICS_1893
&& (info->currentPHY->id1 & 0xfff0) == PHY_ID1_ICS_1893)
mdio_write(info, 0x0018, 0xD200);
// SiS 630E has some bugs on default value of PHY registers
if (info->pciInfo->revision == SiS900_REVISION_SiS630E) {
mdio_write(info, MII_AUTONEG_ADV, 0x05e1);
mdio_write(info, MII_CONFIG1, 0x22);
mdio_write(info, MII_CONFIG2, 0xff00);
mdio_write(info, MII_MASK, 0xffc0);
}
*/
write32(device->reg_base + WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BURSTLEN_16LONG);
device->link = mii_readstatus(device) & MII_STATUS_LINK;
return B_OK;
}
void
wb_init(wb_device *device)
{
LOG((DEVICE_NAME": init()\n"));
wb_reset(device);
device->wb_txthresh = WB_TXTHRESH_INIT;
switch(device->wb_cachesize) {
case 32:
@@ -131,6 +247,9 @@ wb_reset(wb_device *device)
break;
}
write32(device->reg_base + WB_BUSCTL, WB_BUSCTL_MUSTBEONE|WB_BUSCTL_ARBITRATION);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BURSTLEN_16LONG);
write32(device->reg_base + WB_BUSCTL_SKIPLEN, WB_SKIPLEN_4LONG);
/* Early TX interrupt; doesn't tend to work too well at 100Mbps */
@@ -150,7 +269,36 @@ wb_reset(wb_device *device)
//Disable capture broadcast
WB_CLRBIT(device->reg_base + WB_NETCFG, WB_NETCFG_RX_BROAD);
}
void
wb_reset(wb_device *device)
{
int i = 0;
cpu_status status;
LOG((DEVICE_NAME": reset()\n"));
status = disable_interrupts();
// XXX: What about a spinlock here ?
write32(device->reg_base + WB_NETCFG, 0L);
write32(device->reg_base + WB_BUSCTL, 0L);
write32(device->reg_base + WB_TXADDR, 0L);
write32(device->reg_base + WB_RXADDR, 0L);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
WB_SETBIT(device->reg_base + WB_BUSCTL, WB_BUSCTL_RESET);
for (i = 0; i < WB_TIMEOUT; i++) {
if (!(read32(device->reg_base + WB_BUSCTL) & WB_BUSCTL_RESET))
break;
}
if (i == WB_TIMEOUT)
LOG(("reset hasn't completed!!!"));
restore_interrupts(status);
/* Wait a bit while the chip reorders his toughts */
@@ -159,9 +307,42 @@ wb_reset(wb_device *device)
//XXX Initialize MII interface
}
void
wb_updateLink(struct wb_device *device)
{
{
if (!device->autoNegotiationComplete) {
int32 mode = wb_read_mode(device);
if (mode)
wb_set_mode(device, mode);
return;
}
if (device->link) { // link lost
uint16 status = mii_readstatus(device);
if ((status & MII_STATUS_LINK) == 0) {
device->link = false;
dprintf(DEVICE_NAME ": link lost\n");
// if it's the internal SiS900 MII PHY, reset it
//if (device->currentPHY->id0 == PHY_ID0_SiS900_INTERNAL
// && (device->currentPHY->id1 & 0xfff0) == PHY_ID1_SiS900_INTERNAL)
// sis900_resetPHY(device);
}
}
if (!device->link) { // new link established
uint16 status;
wb_selectPHY(device);
status = mii_readstatus(device);
if (status & MII_STATUS_LINK) {
//sis900_checkMode(device);
device->link = true;
}
}
}
@@ -362,17 +543,19 @@ wb_create_semaphores(struct wb_device *device)
{
device->rxSem = create_sem(0, "wb840 receive");
if (device->rxSem < B_OK) {
LOG(("Couldn't create sem, sem_id %d\n", (int)device->rxSem));
LOG(("Couldn't create sem, sem_id %ld\n", device->rxSem));
return device->rxSem;
}
set_sem_owner(device->rxSem, B_SYSTEM_TEAM);
device->txSem = create_sem(WB_TX_LIST_CNT, "wb840 transmit");
if (device->txSem < B_OK) {
LOG(("Couldn't create sem, sem_id %d\n", (int)device->txSem));
LOG(("Couldn't create sem, sem_id %ld\n", device->txSem));
delete_sem(device->rxSem);
return device->txSem;
}
set_sem_owner(device->txSem, B_SYSTEM_TEAM);
device->rxLock = 0;
@@ -387,9 +570,10 @@ wb_create_rings(struct wb_device *device)
{
int i;
if ((device->rxArea = create_area("wb840 rx buffer", (void **)&device->rxBuffer[0],
device->rxArea = create_area("wb840 rx buffer", (void **)&device->rxBuffer[0],
B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_RX_LIST_CNT),
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK)
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
if (device->rxArea < B_OK)
return device->rxArea;
for (i = 1; i < WB_RX_LIST_CNT; i++)
@@ -404,9 +588,10 @@ wb_create_rings(struct wb_device *device)
device->rxFree = WB_RX_LIST_CNT;
device->rxDescriptor[WB_RX_LIST_CNT - 1].wb_ctl |= WB_RXCTL_RLAST;
if ((device->txArea = create_area("wb840 tx buffer", (void **)&device->txBuffer[0],
device->txArea = create_area("wb840 tx buffer", (void **)&device->txBuffer[0],
B_ANY_KERNEL_ADDRESS, ROUND_TO_PAGE_SIZE(WB_BUFBYTES * WB_TX_LIST_CNT),
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA)) < B_OK) {
B_FULL_LOCK, B_READ_AREA | B_WRITE_AREA);
if (device->txArea < B_OK) {
delete_area(device->rxArea);
return device->txArea;
}
@@ -444,3 +629,79 @@ wb_delete_rings(struct wb_device *device)
delete_area(device->rxArea);
delete_area(device->txArea);
}
int32
wb_read_mode(wb_device *info)
{
uint16 autoAdv, autoLinkPartner;
int32 speed, duplex;
uint16 status = mii_readstatus(info);
if (!(status & MII_STATUS_LINK)) {
//dprintf(DEVICE_NAME ": no link detected (status = %x)\n", status);
return 0;
}
// auto negotiation completed
autoAdv = wb_miibus_readreg(info, info->phy, MII_AUTONEG_ADV);
autoLinkPartner = wb_miibus_readreg(info, info->phy, MII_AUTONEG_LINK_PARTNER);
status = autoAdv & autoLinkPartner;
speed = status & (MII_NWAY_TX | MII_NWAY_TX_FDX) ? LINK_SPEED_100_MBIT : LINK_SPEED_10_MBIT;
duplex = status & (MII_NWAY_TX_FDX | MII_NWAY_T_FDX) ? LINK_FULL_DUPLEX : LINK_HALF_DUPLEX;
dprintf("status: 0x%x\n", status);
info->autoNegotiationComplete = true;
// workaround for Realtek RTL8201 PHY issue
/*if (phy->id0 == PHY_ID0_REALTEK_8201 && (phy->id1 & 0xFFF0) == PHY_ID1_REALTEK_8201) {
if (mdio_read(info, MII_CONTROL) & MII_CONTROL_FULL_DUPLEX)
duplex = LINK_FULL_DUPLEX;
if (mdio_read(info, 0x0019) & 0x01)
speed = LINK_SPEED_100_MBIT;
}
*/
//dprintf(DEVICE_NAME ": linked, 10%s MBit, %s duplex\n",
// speed == LINK_SPEED_100_MBIT ? "0" : "",
// duplex == LINK_FULL_DUPLEX ? "full" : "half");
return speed | duplex;
}
void
wb_set_mode(wb_device *info, int mode)
{
/*uint32 address = (uint32)info->registers + SiS900_MAC_CONFIG;
uint32 txFlags = SiS900_Tx_AUTO_PADDING | SiS900_Tx_FILL_THRES;
uint32 rxFlags = 0;
int32 speed = mode & LINK_SPEED_MASK;
if (read32(address) & SiS900_MAC_CONFIG_EDB_MASTER) {
TRACE((DEVICE_NAME ": EDB master is set!\n"));
txFlags |= 5 << SiS900_DMA_SHIFT;
rxFlags = 5 << SiS900_DMA_SHIFT;
}
// link speed FIFO thresholds
if (speed == LINK_SPEED_HOME || speed == LINK_SPEED_10_MBIT) {
rxFlags |= SiS900_Rx_10_MBIT_DRAIN_THRES;
txFlags |= SiS900_Tx_10_MBIT_DRAIN_THRES;
} else {
rxFlags |= SiS900_Rx_100_MBIT_DRAIN_THRES;
txFlags |= SiS900_Tx_100_MBIT_DRAIN_THRES;
}
// duplex mode
if ((mode & LINK_DUPLEX_MASK) == LINK_FULL_DUPLEX) {
txFlags |= SiS900_Tx_CS_IGNORE | SiS900_Tx_HB_IGNORE;
rxFlags |= SiS900_Rx_ACCEPT_Tx_PACKETS;
}
write32((uint32)info->registers + SiS900_MAC_Tx_CONFIG, txFlags);
write32((uint32)info->registers + SiS900_MAC_Rx_CONFIG, rxFlags);
*/
}
+117 -11
View File
@@ -197,17 +197,13 @@ enum interruptMaskBits {
WB_IMR_ABNORMAL = 0x00008000,
WB_IMR_NORMAL = 0x00010000,
};
#define WB_INTRS \
(WB_IMR_RX_OK|WB_IMR_RX_IDLE|WB_IMR_RX_ERR|WB_IMR_TX_OK| \
WB_IMR_RX_NOBUF|WB_IMR_RX_ERR|WB_IMR_RX_EARLY| \
WB_IMR_TX_NOBUF|WB_IMR_TX_UNDERRUN|WB_IMR_BUS_ERR| \
WB_IMR_ABNORMAL|WB_IMR_NORMAL|WB_IMR_TX_EARLY)
/*#define WB_INTRS \
(WB_IMR_RX_OK|WB_IMR_TX_OK|WB_IMR_RX_ERR| \
WB_IMR_TX_UNDERRUN|WB_IMR_BUS_ERR| \
WB_IMR_ABNORMAL|WB_IMR_NORMAL|WB_IMR_TX_EARLY)
*/
/*
* Serial I/O (EEPROM/ROM) bits.
*/
@@ -300,11 +296,12 @@ struct wb_desc {
#define WB_MAX_FRAMELEN 1536
#define WB_UNSENT 0x1234
#define WB_BUFBYTES (1024 * sizeof(uint32))
#define WB_BUFBYTES 2048
/* Ethernet defines */
#define CRC_SIZE 4
#define ETHER_TRANSMIT_TIMEOUT ((bigtime_t)5000000) /* five seconds */
#define WB_TIMEOUT 1000
typedef struct wb_mii_frame wb_mii_frame;
struct wb_mii_frame {
@@ -355,9 +352,15 @@ struct wb_device {
int16 txInterruptIndex;
int16 txSent;
struct mii_phy *firstPHY;
struct mii_phy *currentPHY;
uint16 phy;
bool autoNegotiationComplete;
bool link;
uint16 fixedMode;
volatile int32 blockFlag;
ether_address_t myaddr; /* my ethernet address */
//volatile int interrupted; /* interrupted system call */
ether_address_t myaddr;
spinlock intLock;
const char* deviceName;
@@ -366,7 +369,99 @@ struct wb_device {
int wb_cachesize;
};
#define WB_TIMEOUT 1000
/* MII Interface */
struct mii_phy {
struct mii_phy *next;
uint16 id0, id1;
uint16 address;
uint8 types;
};
// taken from Axel's Sis900 driver
enum MII_address {
// standard registers
MII_CONTROL = 0x00,
MII_STATUS = 0x01,
MII_PHY_ID0 = 0x02,
MII_PHY_ID1 = 0x03,
MII_AUTONEG_ADV = 0x04,
MII_AUTONEG_LINK_PARTNER = 0x05,
MII_AUTONEG_EXT = 0x06
// SiS900 specific registers
/*MII_CONFIG1 = 0x10,
MII_CONFIG2 = 0x11,
MII_LINK_STATUS = 0x12,
MII_MASK = 0x13,
MII_RESERVED = 0x14*/
};
enum MII_control {
MII_CONTROL_RESET = 0x8000,
MII_CONTROL_RESET_AUTONEG = 0x0200,
MII_CONTROL_AUTO = 0x1000,
MII_CONTROL_FULL_DUPLEX = 0x0100,
MII_CONTROL_ISOLATE = 0x0400
};
enum MII_commands {
MII_CMD_READ = 0x6000,
MII_CMD_WRITE = 0x5002,
MII_PHY_SHIFT = 7,
MII_REG_SHIFT = 2,
};
enum MII_status_bits {
MII_STATUS_EXT = 0x0001,
MII_STATUS_JAB = 0x0002,
MII_STATUS_LINK = 0x0004,
MII_STATUS_CAN_AUTO = 0x0008,
MII_STATUS_FAULT = 0x0010,
MII_STATUS_AUTO_DONE = 0x0020,
MII_STATUS_CAN_T = 0x0800,
MII_STATUS_CAN_T_FDX = 0x1000,
MII_STATUS_CAN_TX = 0x2000,
MII_STATUS_CAN_TX_FDX = 0x4000,
MII_STATUS_CAN_T4 = 0x8000
};
enum MII_auto_negotiation {
MII_NWAY_NODE_SEL = 0x001f,
MII_NWAY_CSMA_CD = 0x0001,
MII_NWAY_T = 0x0020,
MII_NWAY_T_FDX = 0x0040,
MII_NWAY_TX = 0x0080,
MII_NWAY_TX_FDX = 0x0100,
MII_NWAY_T4 = 0x0200,
MII_NWAY_PAUSE = 0x0400,
MII_NWAY_RF = 0x2000,
MII_NWAY_ACK = 0x4000,
MII_NWAY_NP = 0x8000
};
enum MII_link_status {
MII_LINK_FAIL = 0x4000,
MII_LINK_100_MBIT = 0x0080,
MII_LINK_FULL_DUPLEX = 0x0040
};
enum link_modes {
LINK_HALF_DUPLEX = 0x0100,
LINK_FULL_DUPLEX = 0x0200,
LINK_DUPLEX_MASK = 0xff00,
LINK_SPEED_HOME = 1,
LINK_SPEED_10_MBIT = 10,
LINK_SPEED_100_MBIT = 100,
LINK_SPEED_DEFAULT = LINK_SPEED_100_MBIT,
LINK_SPEED_MASK = 0x00ff
};
/*
* Vendor and Card IDs
@@ -387,13 +482,24 @@ struct wb_device {
// Prototypes
extern int32 wb_interrupt(void *arg);
extern status_t wb_create_semaphores(struct wb_device *device);
extern status_t wb_create_rings(struct wb_device *device);
extern status_t wb_create_semaphores(wb_device *device);
extern status_t wb_create_rings(wb_device *device);
extern void wb_delete_rings(wb_device *device);
extern void wb_init(wb_device *device);
extern void wb_reset(wb_device *device);
extern status_t wb_initPHYs(wb_device *device);
extern void wb_disable_interrupts(wb_device *device);
extern void wb_enable_interrupts(wb_device *device);
extern void wb_set_mode(wb_device *device, int mode);
extern int32 wb_read_mode(wb_device *device);
extern int32 wb_tick(timer *arg);
extern void wb_free_rx_descriptor(wb_desc *desc);
extern void print_address(ether_address_t *addr);
#endif //__WB840_H