📄 if_8139.c
字号:
*/
cyg_drv_interrupt_create(rltk8139_info->vector,
rltk8139_info->isr_priority,
(CYG_ADDRWORD)sc,
rltk8139_isr,
eth_drv_dsr,
&rltk8139_info->interrupt_handle,
&rltk8139_info->interrupt);
cyg_drv_interrupt_attach(rltk8139_info->interrupt_handle);
#endif
}
else {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf(" Does not generate interrupts.\n");
#endif
}
/*
* Call 'cyg_pci_configure_device' for those platforms that do not
* configure the PCI bus during HAL initialization. According to Nick
* Garnett, there are good reasons to not configure PCI devices during HAL
* initialization. Also according to Nick, calling
* 'cyg_pci_configure_device' for devices already configured should have no
* effect and thus is safe to do.
*/
if (cyg_pci_configure_device(&pci_device_info)) {
#ifdef DEBUG_RLTK8139_DRIVER
int i;
diag_printf("Found device #%d on bus %d, devfn 0x%02x:\n", n_th,
CYG_PCI_DEV_GET_BUS(pci_device_id),
CYG_PCI_DEV_GET_DEVFN(pci_device_id));
if (pci_device_info.command & CYG_PCI_CFG_COMMAND_ACTIVE)
diag_printf(" Note that board is active. Probed"
" sizes and CPU addresses are invalid!\n");
diag_printf(" Vendor 0x%04x", pci_device_info.vendor);
diag_printf("\n Device 0x%04x", pci_device_info.device);
diag_printf("\n Command 0x%04x, Status 0x%04x\n",
pci_device_info.command, pci_device_info.status)
;
diag_printf(" Class/Rev 0x%08x", pci_device_info.class_rev);
diag_printf("\n Header 0x%02x\n", pci_device_info.header_type);
diag_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n",
pci_device_info.header.normal.sub_vendor,
pci_device_info.header.normal.sub_id);
for(i = 0; i < CYG_PCI_MAX_BAR; i++) {
diag_printf(" BAR[%d] 0x%08x /", i, pci_device_info.base_address[i]);
diag_printf(" probed size 0x%08x / CPU addr 0x%08x\n",
pci_device_info.base_size[i], pci_device_info.base_map[i]);
}
#endif
}
else {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("Failed to configure 8139 device #%d\n", n_th);
#endif
return false;
}
/*
* Enable memory mapped and port based I/O and busmastering. We currently
* only support IO space accesses; memory mapping is enabled so that bit
* DVRLOAD in CONFIG1 is cleared automatically.
*
* NOTE: it seems that for some configurations/HALs, the device is already
* activated at this point, even though eCos' documentation suggests
* it shouldn't be. At least in my case, this is not a problem.
*/
cyg_pci_read_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
&command);
command |= (CYG_PCI_CFG_COMMAND_IO | CYG_PCI_CFG_COMMAND_MEMORY
| CYG_PCI_CFG_COMMAND_MASTER);
cyg_pci_write_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND,
command);
/*
* This is the base address used to talk to the device. The 8139's IOAR
* and MEMAR registers are BAR0 and BAR1, respectively.
*/
rltk8139_info->base_address = pci_device_info.base_map[0];
/*
* Read the MAC address. The RealTek data sheet seems to claim this should
* only be read in 4 byte accesses, but the code below works just fine.
*/
for (found = 0; found < 6; ++found)
rltk8139_info->mac[found] = INB(rltk8139_info->base_address + IDR0 + found);
/*
* This is the indicator for "uses an interrupt". The code was lifted
* from the eCos Intel 82559 driver.
*/
if (rltk8139_info->interrupt_handle != 0) {
cyg_drv_interrupt_acknowledge(rltk8139_info->vector);
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
cyg_drv_interrupt_unmask(rltk8139_info->vector);
#endif
}
return true;
}
#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE
/*
* Interrupt service routine. We do not clear the interrupt status bits
* (since this should really only be done after handling whatever caused
* the interrupt, and that is done in the '_deliver' routine), but instead
* clear the interrupt mask.
*
* If we are sharing interrupts with other devices, we have two options
* (configurable):
*
* 1. Mask the interrupt vector completly. Personally I think this is a bad
* idea because the other devices sharing this interrupt are also masked
* until the network thread gets around to calling the '_deliver' routine.
*
* 2. Use the interrupt mask register in the 8139 to mask just the interrupt
* request coming from the 8139. This way, the other devices' requests
* can still be serviced.
*/
static cyg_uint32
rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data)
{
Rltk8139_t *rltk8139_info;
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS
cyg_uint16 isr;
#endif
rltk8139_info = (Rltk8139_t *)(((struct eth_drv_sc *)data)->driver_private);
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS
/*
* If interrupt sharing is enabled, check if the interrupt is really
* intended for us. Note that while the RealTek data sheet claims that
* reading the interrupt status register will clear all it's bits,
* this is not true, so we can read it without problems.
*/
if (!(isr = INW(rltk8139_info->base_address + ISR)))
return 0;
#endif
#ifdef CYGPKG_DEVS_ETH_RLTK_8139_MASK_INTERRUPTS_IN_8139
/* Clear the interrupt mask to stop the current request. */
OUTW(0, rltk8139_info->base_address + IMR);
#else
/* Mask the interrupt */
cyg_interrupt_mask(vector);
#endif
/* Acknowledge the interrupt for those platforms were this is necessary */
cyg_interrupt_acknowledge(vector);
return (CYG_ISR_HANDLED | CYG_ISR_CALL_DSR);
}
#endif /* ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE */
/*
* Reset the chip. This function is not exported to higher level software.
*/
static void
rltk8139_reset(struct eth_drv_sc *sc)
{
rltk8139_stop(sc);
rltk8139_start(sc, NULL, 0);
}
#ifdef ETH_DRV_SET_MC_LIST
/*
* I assume (hope !) that this is identical to Ethernet's CRC algorithm
* specified in IEEE 802.3. It does seem to calculate the same CRC that
* the 8139 itself does, so I think it is correct.
* Note that while Ethernet's polynomial is usually specified as 04C11DB7,
* we must use EDB88320 because we shift the bits to the left, not the right.
* (See ftp://ftp.rocksoft.com/papers/crc_v3.txt for a good description of
* CRC algorithms).
*/
static cyg_uint32
ether_crc(cyg_uint8 *data, int length)
{
int bit;
cyg_uint32 crc = 0xFFFFFFFFU;
while (length-- > 0) {
crc ^= *data++;
for (bit = 0; bit < 8; ++bit) {
if (crc & 1)
crc = (crc >> 1) ^ 0xEDB88320U;
else
crc = (crc >> 1);
}
}
return crc ^ 0xFFFFFFFFU;
}
/*
* Set up multicast filtering. The way I understand existing driver code
* (Linux and OpenBSD), the 8139 calculates the ethernet CRC of
* incoming addresses and uses the top 6 bits as an index into a hash
* table. If the corresponding bit is set in MAR0..7, the address is
* accepted.
*/
static void
rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info,
struct eth_drv_mc_list *mc_list)
{
cyg_uint32 mar[2], hash;
int i;
/* If 'mc_list' is NULL, accept all multicast packets. */
if (!mc_list) {
mar[0] = 0xFFFFFFFFU;
mar[1] = 0xFFFFFFFFU;
}
else {
mar[0] = 0;
mar[1] = 0;
for (i = 0; i < mc_list->len; ++i) {
hash = ether_crc(&mc_list->addrs[i][0], ETHER_ADDR_LEN) >> 26;
mar[hash >> 5] |= (1 << (hash & 0x1F));
}
}
/* Program the new filter values */
OUTL(mar[0], rltk8139_info->base_address + MAR0);
OUTL(mar[1], rltk8139_info->base_address + MAR4);
}
#endif /* ifdef ETH_DRV_SET_MC_LIST */
/*
* Initialize the network interface. Since the chips is reset by calling
* _stop() and _start(), any code that will never need to be executed more
* than once after system startup should go here.
*/
static bool
rltk8139_init(struct cyg_netdevtab_entry *tab)
{
struct eth_drv_sc *sc;
Rltk8139_t *rltk8139_info;
sc = (struct eth_drv_sc *)(tab->device_instance);
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* Initialize the eCos PCI library. According to the documentation, it
* is safe to call this function multiple times, so we call it just to
* be sure it has been done.
*/
cyg_pci_init();
/*
* Scan the PCI bus for the specified chip. The '_find' function will also
* do some basic PCI initialization.
*/
if (!rltk8139_find(rltk8139_info->device_num, sc)) {
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_init: could not find RealTek 8139 chip #%d.\n",
rltk8139_info->device_num);
#endif
return false;
}
/* platform depends initialize */
CYGHWR_RLTK_8139_PLF_INIT(sc);
/*
* The initial tx threshold is set here to prevent it from being reset
* with every _start().
*/
rltk8139_info->tx_threshold = 3;
/* Initialize upper level driver */
(sc->funs->eth_drv->init)(sc, rltk8139_info->mac);
return true;
}
/*
* (Re)Start the chip, initializing data structures and enabling the
* transmitter and receiver. Currently, 'flags' is unused by eCos.
*/
static void
rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
Rltk8139_t *rltk8139_info;
int i;
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_start(%s)\n", sc->dev_name);
#endif
rltk8139_info = (Rltk8139_t *)(sc->driver_private);
/*
* Reset the chip. Existing driver code implies that this may take up
* to 10ms; since I don't know under what exact circumstances this code may
* be called I busy wait here regardless.
*/
OUTB(RST, rltk8139_info->base_address + CR);
while (INB(rltk8139_info->base_address + CR) & RST);
#ifdef DEBUG_RLTK8139_DRIVER
diag_printf("rltk8139_start(%s): 8139 was successfully reset.\n",
sc->dev_name);
#endif
/*
* Clear the key storage area. These keys are used by the eCos networking
* support code to keep track of individual packets.
*/
for (i = 0; i < NUM_TX_DESC; ++i)
rltk8139_info->tx_desc_key[i] = 0;
/* Initialize transmission buffer control */
rltk8139_info->tx_free_desc = 0;
rltk8139_info->tx_num_free_desc = NUM_TX_DESC;
/*
* Set the requested MAC address if enaddr != NULL. This is a special
* feature of my '_start' function since it's used to reset the chip after
* errors as well.
*/
if (enaddr != NULL) {
for (i = 0; i < 6; ++i) {
rltk8139_info->mac[i] = enaddr[i];
OUTB(enaddr[i], rltk8139_info->base_address + IDR0 + i);
}
}
/*
* Now setup the transmission and reception buffers. These could be done
* in _init() and kept around, but putting them here fits better logically.
*/
OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 0 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD0);
OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 1 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD1);
OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 2 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD2);
OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer
+ 3 * TX_BUF_SIZE)),
rltk8139_info->base_address + TSAD3);
OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)rltk8139_info->rx_ring),
rltk8139_info->base_address + RBSTART);
/*
* Enable the transmitter and receiver, then clear the missed packet
* counter.
*/
OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
rltk8139_info->base_address + CR);
OUTL(0, rltk8139_info->base_address + MPC);
/*
* It seems the receiver and transmitter configuration can only
* be set after the transmitter/receiver have been enabled.
*/
OUTL(TXCFG, rltk8139_info->base_address + TCR);
OUTL(RXCFG | AM, rltk8139_info->base_address + RCR);
/*
* Enable the transmitter and receiver again. I'm not sure why this is
* necessary; the Linux driver does it so we do it here just to be on
* the safe side.
*/
OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE),
rltk8139_info->base_address + CR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -