📄 rtl8139.c
字号:
#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */#define EE_CS 0x08 /* EEPROM chip select. */#define EE_DATA_WRITE 0x02 /* EEPROM chip data in. */#define EE_WRITE_0 0x00#define EE_WRITE_1 0x02#define EE_DATA_READ 0x01 /* EEPROM chip data out. */#define EE_ENB (0x80 | EE_CS)/* Delay between EEPROM clock transitions. No extra delay is needed with 33Mhz PCI, but 66Mhz may change this.*/#define eeprom_delay() inl(ee_addr)/* The EEPROM commands include the alway-set leading bit. */#define EE_WRITE_CMD (5 << 6)#define EE_READ_CMD (6 << 6)#define EE_ERASE_CMD (7 << 6)static int read_eeprom(int location){ int i; unsigned int retval = 0; long ee_addr = ioaddr + Cfg9346; int read_cmd = location | EE_READ_CMD; outb(EE_ENB & ~EE_CS, ee_addr); outb(EE_ENB, ee_addr); /* Shift the read command bits out. */ for (i = 10; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; outb(EE_ENB | dataval, ee_addr); eeprom_delay(); outb(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); eeprom_delay(); } outb(EE_ENB, ee_addr); eeprom_delay(); for (i = 16; i > 0; i--) { outb(EE_ENB | EE_SHIFT_CLK, ee_addr); eeprom_delay(); retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0); outb(EE_ENB, ee_addr); eeprom_delay(); } /* Terminate the EEPROM access. */ outb(~EE_CS, ee_addr); return retval;}static void rtl_reset(struct nic* nic){ int i; outb(CmdReset, ioaddr + ChipCmd); cur_rx = 0; cur_tx = 0; /* Give the chip 10ms to finish the reset. */ load_timer2(10*TICKS_PER_MS); while ((inb(ioaddr + ChipCmd) & CmdReset) != 0 && timer2_running()) /* wait */; for (i = 0; i < ETH_ALEN; i++) outb(nic->node_addr[i], ioaddr + MAC0 + i); /* Must enable Tx/Rx before setting transfer thresholds! */ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); outl((RX_FIFO_THRESH<<13) | (RX_BUF_LEN_IDX<<11) | (RX_DMA_BURST<<8), ioaddr + RxConfig); /* accept no frames yet! */ outl((TX_DMA_BURST<<8)|0x03000000, ioaddr + TxConfig); /* The Linux driver changes Config1 here to use a different LED pattern * for half duplex or full/autodetect duplex (for full/autodetect, the * outputs are TX/RX, Link10/100, FULL, while for half duplex it uses * TX/RX, Link100, Link10). This is messy, because it doesn't match * the inscription on the mounting bracket. It should not be changed * from the configuration EEPROM default, because the card manufacturer * should have set that to match the card. */#ifdef DEBUG_RX printf("rx ring address is %X\n",(unsigned long)rx_ring);#endif outl((unsigned long)rx_ring, ioaddr + RxBuf); /* Start the chip's Tx and Rx process. */ outl(0, ioaddr + RxMissed); /* set_rx_mode */ outb(AcceptBroadcast|AcceptMyPhys, ioaddr + RxConfig); /* If we add multicast support, the MAR0 register would have to be * initialized to 0xffffffffffffffff (two 32 bit accesses). Etherboot * only needs broadcast (for ARP/RARP/BOOTP/DHCP) and unicast. */ outb(CmdRxEnb | CmdTxEnb, ioaddr + ChipCmd); /* Disable all known interrupts by setting the interrupt mask. */ outw(0, ioaddr + IntrMask);}static void rtl_transmit(struct nic *nic, const char *destaddr, unsigned int type, unsigned int len, const char *data){ unsigned int status, to, nstype; unsigned long txstatus; memcpy(tx_buffer, destaddr, ETH_ALEN); memcpy(tx_buffer + ETH_ALEN, nic->node_addr, ETH_ALEN); nstype = htons(type); memcpy(tx_buffer + 2 * ETH_ALEN, (char*)&nstype, 2); memcpy(tx_buffer + ETH_HLEN, data, len); len += ETH_HLEN;#ifdef DEBUG_TX printf("sending %d bytes ethtype %hX\n", len, type);#endif /* Note: RTL8139 doesn't auto-pad, send minimum payload (another 4 * bytes are sent automatically for the FCS, totalling to 64 bytes). */ while (len < ETH_ZLEN) { tx_buffer[len++] = '\0'; } outl((unsigned long)tx_buffer, ioaddr + TxAddr0 + cur_tx*4); outl(((TX_FIFO_THRESH<<11) & 0x003f0000) | len, ioaddr + TxStatus0 + cur_tx*4); to = currticks() + RTL_TIMEOUT; do { status = inw(ioaddr + IntrStatus); /* Only acknlowledge interrupt sources we can properly handle * here - the RxOverflow/RxFIFOOver MUST be handled in the * rtl_poll() function. */ outw(status & (TxOK | TxErr | PCIErr), ioaddr + IntrStatus); if ((status & (TxOK | TxErr | PCIErr)) != 0) break; } while (currticks() < to); txstatus = inl(ioaddr+ TxStatus0 + cur_tx*4); if (status & TxOK) { cur_tx = (cur_tx + 1) % NUM_TX_DESC;#ifdef DEBUG_TX printf("tx done (%d ticks), status %hX txstatus %X\n", to-currticks(), status, txstatus);#endif } else {#ifdef DEBUG_TX printf("tx timeout/error (%d ticks), status %hX txstatus %X\n", currticks()-to, status, txstatus);#endif rtl_reset(nic); }}static int rtl_poll(struct nic *nic){ unsigned int status; unsigned int ring_offs; unsigned int rx_size, rx_status; if (inb(ioaddr + ChipCmd) & RxBufEmpty) { return 0; } status = inw(ioaddr + IntrStatus); /* See below for the rest of the interrupt acknowledges. */ outw(status & ~(RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus);#ifdef DEBUG_RX printf("rtl_poll: int %hX ", status);#endif ring_offs = cur_rx % RX_BUF_LEN; rx_status = *(unsigned int*)(rx_ring + ring_offs); rx_size = rx_status >> 16; rx_status &= 0xffff; if ((rx_status & (RxBadSymbol|RxRunt|RxTooLong|RxCRCErr|RxBadAlign)) || (rx_size < ETH_ZLEN) || (rx_size > ETH_FRAME_LEN + 4)) { printf("rx error %hX\n", rx_status); rtl_reset(nic); /* this clears all interrupts still pending */ return 0; } /* Received a good packet */ nic->packetlen = rx_size - 4; /* no one cares about the FCS */ if (ring_offs+4+rx_size-4 > RX_BUF_LEN) { int semi_count = RX_BUF_LEN - ring_offs - 4; memcpy(nic->packet, rx_ring + ring_offs + 4, semi_count); memcpy(nic->packet+semi_count, rx_ring, rx_size-4-semi_count);#ifdef DEBUG_RX printf("rx packet %d+%d bytes", semi_count,rx_size-4-semi_count);#endif } else { memcpy(nic->packet, rx_ring + ring_offs + 4, nic->packetlen);#ifdef DEBUG_RX printf("rx packet %d bytes", rx_size-4);#endif }#ifdef DEBUG_RX printf(" at %X type %hhX%hhX rxstatus %hX\n", (unsigned long)(rx_ring+ring_offs+4), nic->packet[12], nic->packet[13], rx_status);#endif cur_rx = (cur_rx + rx_size + 4 + 3) & ~3; outw(cur_rx - 16, ioaddr + RxBufPtr); /* See RTL8139 Programming Guide V0.1 for the official handling of * Rx overflow situations. The document itself contains basically no * usable information, except for a few exception handling rules. */ outw(status & (RxFIFOOver | RxOverflow | RxOK), ioaddr + IntrStatus); return 1;}static void rtl_disable(struct nic *nic){ /* reset the chip */ outb(CmdReset, ioaddr + ChipCmd); /* 10 ms timeout */ load_timer2(10*TICKS_PER_MS); while ((inb(ioaddr + ChipCmd) & CmdReset) != 0 && timer2_running()) /* wait */;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -