📄 3c90x.c
字号:
}/*** a3c90x_internal_WriteEepromWord - write a physical word of *** data to the onboard serial eeprom (not the BIOS prom, but the *** nvram in the card that stores, among other things, the MAC *** address). ***/static inta3c90x_internal_WriteEepromWord(int ioaddr, int address, unsigned short value) { /** Select register window **/ a3c90x_internal_SetWindow(ioaddr, winEepromBios0); /** Verify Eeprom not busy **/ while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); /** Issue WriteEnable, and wait for completion. **/ outw(0x30, ioaddr + regEepromCommand_0_w); while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); /** Issue EraseRegister, and wait for completion. **/ outw(address + ((0x03)<<6), ioaddr + regEepromCommand_0_w); while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); /** Send the new data to the eeprom, and wait for completion. **/ outw(value, ioaddr + regEepromData_0_w); outw(0x30, ioaddr + regEepromCommand_0_w); while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); /** Burn the new data into the eeprom, and wait for completion. **/ outw(address + ((0x01)<<6), ioaddr + regEepromCommand_0_w); while((1<<15) & inw(ioaddr + regEepromCommand_0_w)); return 0; }/*** a3c90x_internal_WriteEeprom - write data to the serial eeprom, *** and re-compute the eeprom checksum. ***/static inta3c90x_internal_WriteEeprom(int ioaddr, int address, unsigned short value) { int cksum = 0,v; int i; int maxAddress, cksumAddress; if (INF_3C90X.isBrev) { maxAddress=0x1f; cksumAddress=0x20; } else { maxAddress=0x16; cksumAddress=0x17; } /** Write the value. **/ if (a3c90x_internal_WriteEepromWord(ioaddr, address, value) == -1) return -1; /** Recompute the checksum. **/ for(i=0;i<=maxAddress;i++) { v = a3c90x_internal_ReadEeprom(ioaddr, i); cksum ^= (v & 0xFF); cksum ^= ((v>>8) & 0xFF); } /** Write the checksum to the location in the eeprom **/ if (a3c90x_internal_WriteEepromWord(ioaddr, cksumAddress, cksum) == -1) return -1; return 0; }/*** a3c90x_reset: exported function that resets the card to its default *** state. This is so the Linux driver can re-set the card up the way *** it wants to. If CFG_3C90X_PRESERVE_XCVR is defined, then the reset will *** not alter the selected transceiver that we used to download the boot *** image. ***/static voida3c90x_reset(struct nic *nic) { int cfg;#ifdef CFG_3C90X_PRESERVE_XCVR /** Read the current InternalConfig value. **/ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); cfg = inl(INF_3C90X.IOAddr + regInternalConfig_3_l);#endif /** Send the reset command to the card **/ printf("Issuing RESET:\n"); a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdGlobalReset, 0); /** wait for reset command to complete **/ while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); /** global reset command resets station mask, non-B revision cards ** require explicit reset of values **/ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winAddressing2); outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+0); outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+2); outw(0, INF_3C90X.IOAddr + regStationMask_2_3w+4);#ifdef CFG_3C90X_PRESERVE_XCVR /** Re-set the original InternalConfig value from before reset **/ a3c90x_internal_SetWindow(INF_3C90X.IOAddr, winTxRxOptions3); outl(cfg, INF_3C90X.IOAddr + regInternalConfig_3_l); /** enable DC converter for 10-Base-T **/ if ((cfg&0x0300) == 0x0300) { a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdEnableDcConverter, 0); }#endif /** Issue transmit reset, wait for command completion **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxReset, 0); while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS) ; if (! INF_3C90X.isBrev) outb(0x01, INF_3C90X.IOAddr + regTxFreeThresh_b); a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); /** ** reset of the receiver on B-revision cards re-negotiates the link ** takes several seconds (a computer eternity) **/ if (INF_3C90X.isBrev) a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x04); else a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxReset, 0x00); while (inw(INF_3C90X.IOAddr + regCommandIntStatus_w) & INT_CMDINPROGRESS); ; a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdRxEnable, 0); a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetInterruptEnable, 0); /** enable rxComplete and txComplete **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdSetIndicationEnable, 0x0014); /** acknowledge any pending status flags **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdAcknowledgeInterrupt, 0x661); return; }/*** a3c90x_transmit: exported function that transmits a packet. Does not *** return any particular status. Parameters are: *** d[6] - destination address, ethernet; *** t - protocol type (ARP, IP, etc); *** s - size of the non-header part of the packet that needs transmitted; *** p - the pointer to the packet data itself. ***/static voida3c90x_transmit(struct nic *nic, const char *d, unsigned int t, unsigned int s, const char *p) { struct eth_hdr { unsigned char dst_addr[ETH_ALEN]; unsigned char src_addr[ETH_ALEN]; unsigned short type; } hdr; unsigned char status; unsigned i, retries; for (retries=0; retries < XMIT_RETRIES ; retries++) { /** Stall the download engine **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 2); /** Make sure the card is not waiting on us **/ inw(INF_3C90X.IOAddr + regCommandIntStatus_w); inw(INF_3C90X.IOAddr + regCommandIntStatus_w); while (inw(INF_3C90X.IOAddr+regCommandIntStatus_w) & INT_CMDINPROGRESS) ; /** Set the ethernet packet type **/ hdr.type = htons(t); /** Copy the destination address **/ memcpy(hdr.dst_addr, d, ETH_ALEN); /** Copy our MAC address **/ memcpy(hdr.src_addr, INF_3C90X.HWAddr, ETH_ALEN); /** Setup the DPD (download descriptor) **/ INF_3C90X.TransmitDPD.DnNextPtr = 0; /** set notification for transmission completion (bit 15) **/ INF_3C90X.TransmitDPD.FrameStartHeader = (s + sizeof(hdr)) | 0x8000; INF_3C90X.TransmitDPD.HdrAddr = virt_to_bus(&hdr); INF_3C90X.TransmitDPD.HdrLength = sizeof(hdr); INF_3C90X.TransmitDPD.DataAddr = virt_to_bus(p); INF_3C90X.TransmitDPD.DataLength = s + (1<<31); /** Send the packet **/ outl(virt_to_bus(&(INF_3C90X.TransmitDPD)), INF_3C90X.IOAddr + regDnListPtr_l); /** End Stall and Wait for upload to complete. **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdStallCtl, 3); while(inl(INF_3C90X.IOAddr + regDnListPtr_l) != 0) ; /** Wait for NIC Transmit to Complete **/ load_timer2(10*TICKS_PER_MS); /* Give it 10 ms */ while (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004) && timer2_running()) ; if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0004)) { printf("3C90X: Tx Timeout\n"); continue; } status = inb(INF_3C90X.IOAddr + regTxStatus_b); /** acknowledge transmit interrupt by writing status **/ outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); /** successful completion (sans "interrupt Requested" bit) **/ if ((status & 0xbf) == 0x80) return; printf("3C90X: Status (%hhX)\n", status); /** check error codes **/ if (status & 0x02) { printf("3C90X: Tx Reclaim Error (%hhX)\n", status); a3c90x_reset(NULL); } else if (status & 0x04) { printf("3C90X: Tx Status Overflow (%hhX)\n", status); for (i=0; i<32; i++) outb(0x00, INF_3C90X.IOAddr + regTxStatus_b); /** must re-enable after max collisions before re-issuing tx **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); } else if (status & 0x08) { printf("3C90X: Tx Max Collisions (%hhX)\n", status); /** must re-enable after max collisions before re-issuing tx **/ a3c90x_internal_IssueCommand(INF_3C90X.IOAddr, cmdTxEnable, 0); } else if (status & 0x10) { printf("3C90X: Tx Underrun (%hhX)\n", status); a3c90x_reset(NULL); } else if (status & 0x20) { printf("3C90X: Tx Jabber (%hhX)\n", status); a3c90x_reset(NULL); } else if ((status & 0x80) != 0x80) { printf("3C90X: Internal Error - Incomplete Transmission (%hhX)\n", status); a3c90x_reset(NULL); } } /** failed after RETRY attempts **/ printf("Failed to send after %d retries\n", retries); return; }/*** a3c90x_poll: exported routine that waits for a certain length of time *** for a packet, and if it sees none, returns 0. This routine should *** copy the packet to nic->packet if it gets a packet and set the size *** in nic->packetlen. Return 1 if a packet was found. ***/static inta3c90x_poll(struct nic *nic) { int i, errcode; if (!(inw(INF_3C90X.IOAddr + regCommandIntStatus_w)&0x0010)) { return 0; } /** we don't need to acknowledge rxComplete -- the upload engine ** does it for us. **/ /** Build the up-load descriptor **/ INF_3C90X.ReceiveUPD.UpNextPtr = 0; INF_3C90X.ReceiveUPD.UpPktStatus = 0; INF_3C90X.ReceiveUPD.DataAddr = virt_to_bus(nic->packet);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -