📄 if_8139.c
字号:
* 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_uint32rltk8139_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 voidrltk8139_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_uint32ether_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 voidrltk8139_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 boolrltk8139_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 voidrltk8139_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);#ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* * If this driver was not compiled in stand alone (without interrupts) * mode, enable interrupts. */ OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR);#endif#ifdef DEBUG_RLTK8139_DRIVER rltk8139_print_state(sc);#endif}/* * Stop the chip, disabling the transmitter and receiver. */static voidrltk8139_stop(struct eth_drv_sc *sc){ Rltk8139_t *rltk8139_info;#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_stop(%s)\n", sc->dev_name);#endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* Disable receiver and transmitter */ OUTB(INB(rltk8139_info->base_address + CR) & ~(TE|RE), rltk8139_info->base_address + CR); /* Mask all interrupts */ OUTW(0, rltk8139_info->base_address + IMR);}/* * 8139 control function. Unlike a 'real' ioctl function, this function is * not required to tell the caller why a request failed, only that it did * (see the eCos documentation). */static intrltk8139_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length){ int i; Rltk8139_t *rltk8139_info;#ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_control(%08x, %lx)\n", sc, key);#endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); switch (key) {#ifdef ETH_DRV_SET_MAC_ADDRESS case ETH_DRV_SET_MAC_ADDRESS: if ( 6 != data_length ) return 1; /* Set the mac address */ for (i = 0; i < 6; ++i) { rltk8139_info->mac[i] = *(((cyg_uint8 *)data) + i); OUTB(rltk8139_info->mac[i], rltk8139_info->base_address + IDR0 + i); } return 0;#endif#ifdef ETH_DRV_GET_MAC_ADDRESS case ETH_DRV_GET_MAC_ADDRESS: if (6 != data_length) return 1; memcpy(data, rltk8139_info->mac, 6); return 0;#endif#ifdef ETH_DRV_GET_IF_STATS_UD case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE //ETH_STATS_INIT( sc ); // so UPDATE the statistics structure#endif // drop through#ifdef ETH_DRV_GET_IF_STATS case ETH_DRV_GET_IF_STATS:#endif#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) break;#endif#ifdef ETH_DRV_SET_MC_LIST case ETH_DRV_SET_MC_LIST: /* * Program the 8139's multicast filter register. If the eth_drv_mc_list * contains at least one element, set the accept multicast bit in the * receive config register. */ rltk8139_set_multicast_list(rltk8139_info, (struct eth_drv_mc_list *)data); if (((struct eth_drv_mc_list *)data)->len > 0) OUTL(INL(rltk8139_info->base_address + RCR) | AM, rltk8139_info->base_address + RCR); else OUTL(INL(rltk8139_info->base_address + RCR) & ~AM, rltk8139_info->base_address + RCR); return 0;#endif // ETH_DRV_SET_MC_LIST#ifdef ETH_DRV_SET_MC_ALL case ETH_DRV_SET_MC_ALL: /* * Set the accept multicast bit in the receive config register and * program the multicast filter to accept all addresses. */ rltk8139_set_multicast_list(rltk8139_info, NULL); OUTL(INL(rltk8139_info->base_address + RCR) | AM, rltk8139_info->base_address + RCR); return 0;#endif // ETH_DRV_SET_MC_ALL default: return 1; } return 1;}/* * Check if a new packet can be sent. */static intrltk8139_can_send(struct eth_drv_sc *sc){ return ((Rltk8139_t *)(sc->driver_private))->tx_num_free_desc;}/* * Send a packet over the wire. */static voidrltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key){ Rltk8139_t *rltk8139_info; cyg_uint8 *tx_buffer; struct eth_drv_sg *last_sg; int desc;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -