/* AMD PCNetII Driver Author: TomAwezome Driver is based on: - minexew's ShrineOS PCNet implementation - OSDev AMD_PCNET documentation - AMD PCnet(TM)-PCI datasheet - any other useful sources. Guidelines: - Magic numbers are bad. #defines are good. - Understandability over LOC. - Clear documentation. */ #define PCNET_DEBUG_OUTPUT 0 #define PCNET_CMDf_IOEN 0 #define PCNET_CMDf_BMEN 2 #define PCNET_CMDF_IOEN (1 << PCNET_CMDf_IOEN) #define PCNET_CMDF_BMEN (1 << PCNET_CMDf_BMEN) #define PCNET_WD_RESET 0x14 // reset reg location when card is in 16-bit mode #define PCNET_DW_RDP 0x10 #define PCNET_DW_RAP 0x14 #define PCNET_DW_BDP 0x1c #define PCNET_DW_RESET 0x18 // reset reg location when card is in 32-bit mode #define PCNET_CSR_CTRLSTATUS 0 #define PCNET_CSR_INTERRUPTS 3 #define PCNET_CSR_FEATURECTRL 4 #define PCNET_CSR_LADRF0 8 #define PCNET_CSR_LADRF1 9 #define PCNET_CSR_LADRF2 10 #define PCNET_CSR_LADRF3 11 #define PCNET_CSR_PADR0 12 #define PCNET_CSR_PADR1 13 #define PCNET_CSR_PADR2 14 #define PCNET_CSR_MODE 15 #define PCNET_CSR_BADRL 24 #define PCNET_CSR_BADRU 25 #define PCNET_CSR_BADTL 30 #define PCNET_CSR_BADTU 31 #define PCNET_CSR_POLLINT 47 #define PCNET_CSR_SOFTWARESTYLE 58 #define PCNET_CSR_RXRINGLEN 76 #define PCNET_CSR_TXRINGLEN 78 #define PCNET_SWSTYLE_SELECTION 2 // (value, not bit) AMD PCNet datasheet p. 1-968 #define PCNET_SWSTYLE_SSIZE32 8 // Bit 8 of SWSTYLE // Refer to AMD PCNet datasheet p. 1-954, 1-956, 1-957 for Interrupt Mask details. #define PCNET_INT_BSWP 2 // Byte Swap (Big-Endian / Little-Endian) #define PCNET_INT_IDONM 8 // Initialization Done Mask #define PCNET_INT_TINTM 9 // Transmit Interrupt Mask #define PCNET_INT_RINTM 10 // Receive Interrupt Mask #define PCNET_FEATURE_APADXMT 11 #define PCNET_CTRL_INIT 0 #define PCNET_CTRL_STRT 1 #define PCNET_CTRL_STOP 2 #define PCNET_CTRL_IENA 6 #define PCNET_CTRL_IDON 8 #define PCNET_CTRL_RINT 10 #define PCNET_RX_BUFF_COUNT 64 #define PCNET_TX_BUFF_COUNT 64 #define PCNET_DESCRIPTORf_ENP 24 #define PCNET_DESCRIPTORf_STP 25 #define PCNET_DESCRIPTORf_OWN 31 // AMD PCNet datasheet p.1-992, 1-994 class CPCNet { CPciDevInfo *pci; U8 mac_address[6]; // MAC address is first 6 bytes of PCNet EEPROM (page # ? ) I64 current_rx_de_index; // Current Receive DE being processed. Gets incremented, wrapped to 0 at max of PCNET_RX_BUFF_COUNT. I64 current_tx_de_index; // Current Transmit DE being processed. Gets incremented, wrapped to 0 at max of PCNET_TX_BUFF_COUNT. U8 *rx_de_buffer; // Uncached-alias of pointer to the buffer of RX Descriptor Entries. U8 *tx_de_buffer; // Uncached-alias of pointer to the buffer of TX Descriptor Entries. U8 *rx_de_buffer_phys; // Pointer to the buffer of RX Descriptor Entries. (Code Heap, lower 2Gb) U8 *tx_de_buffer_phys; // Pointer to the buffer of TX Descriptor Entries. (Code Heap, lower 2Gb) U32 rx_buffer_addr_phys; // Physical address of actual receive buffers (< 4 Gb) U32 tx_buffer_addr_phys; // Physical address of actual transmit buffers (< 4 Gb) } pcnet; // pcnet is the global variable we store all of this into. class CPCNetDescriptorEntry { /* AMD PCNet datasheet p.1-991 & p.1-994 NOTE: chart typo on 1-994, see ONES and BCNT on 1-995. TX and RX DE's are the same size (16-Bytes) and structure, but have different registers and functions. The RX and TX DE buffers of the CPCNet class are allocated to a certain amount of these DEs. */ U32 buffer_addr; U32 status1; U32 status2; U32 reserved; }; class CPCNetBufferSetup { U16 mode; U8 rlen; U8 tlen; U8 mac[6]; U16 reserved; U8 ladr[8]; U32 rxbuf; U32 txbuf; }; CPCIDev *PCNetPCIDevFind() {// Find and return PCNetII card as a CPCIDev pointer. return PCIDevFind(,, PCIV_PCNET, PCID_PCNET); } U32 PCNetIOBaseGet() { /* Return memory IO base address of PCNet card. Bits 0-4 are not for the IO base, so an AND with ~0x1F ignores those bits. */ U32 io_base = pcnet.pci->bar[0] & ~0x1F; return io_base; } U0 PCNetRst() { /* Reads the 32- and 16-bit RESET registers, which, regardless of which mode the card is in, will reset it back to 16-bit mode. */ InU32(PCNetIOBaseGet + PCNET_DW_RESET); InU16(PCNetIOBaseGet + PCNET_WD_RESET); Busy(5); // OSDev says minimum 1 uS } U0 PCNet32BitModeEnable() { /* AMD PCNet datasheet p. 1-930 Summary: A 32-bit write (while in 16-bit mode) to RDP will cause 16-bit mode exit and immediate enter into 32-bit mode. */ OutU32(PCNetIOBaseGet + PCNET_DW_RDP, 0); } U0 PCNetRAPWrite(U32 value) { /* AMD PCNet datasheet p. 1-952 Summary: Register Address Pointer register value will indicate which CSR / BCR register we want to access in RDP / BDP. */ OutU32(PCNetIOBaseGet + PCNET_DW_RAP, value); } U0 PCNetBCRWrite(U32 bcr, U32 value) { /* AMD PCNet datasheet p. 1-952 Summary: Bus Control Registers are accessed via the BDP (Bus Data Port). Which BCR is selected is based on the value in the RAP. */ PCNetRAPWrite(bcr); OutU32(PCNetIOBaseGet + PCNET_DW_BDP, value); } U32 PCNetBCRRead(U32 bcr) { /* AMD PCNet datasheet p. 1-952 Summary: Bus Control Registers are accessed via the BDP (Bus Data Port). Which BCR is selected is based on the value in the RAP. */ PCNetRAPWrite(bcr); return InU32(PCNetIOBaseGet + PCNET_DW_BDP); } U0 PCNetCSRWrite(U32 csr, U32 value) { /* AMD PCNet datasheet p. 1-952 Summary: Control and Status Registers are accessed via the RDP (Register Data Port). Which CSR is selected is based on the value in the RAP. */ PCNetRAPWrite(csr); OutU32(PCNetIOBaseGet + PCNET_DW_RDP, value); } U32 PCNetCSRRead(U32 csr) { /* AMD PCNet datasheet p. 1-952 Summary: Control and Status Registers are accessed via the RDP (Register Data Port). Which CSR is selected is based on the value in the RAP. */ PCNetRAPWrite(csr); return InU32(PCNetIOBaseGet + PCNET_DW_RDP); } U0 PCNetSWStyleSet() { /* AMD PCNet datasheet p. 1-968 In CSR58 (Software Style), the 8-bit SWSTYLE register dictates interpretation of certain bits in the CSR space, and widths of descriptors and initialization block. In PCINet-PCI mode, CSR4 bits function as defined in the datasheet , and TMD1[29] functions as ADD_FCS. */ U32 csr = PCNetCSRRead(PCNET_CSR_SOFTWARESTYLE); csr &= ~0xFF; // clears first 8 bits: SWSTYLE 8-bit register. csr |= PCNET_SWSTYLE_SELECTION; // set SWSTYLE to PCNet-PCI mode. Bts(&csr, PCNET_SWSTYLE_SSIZE32); // set SSIZE32 bit 1 PCNetCSRWrite(PCNET_CSR_SOFTWARESTYLE, csr); } U0 PCNetMACGet() { /* AMD PCNet datasheet p. 1-887, 1-931, 1-937 MAC address stored at first 6 bytes of PCNet EEPROM. EEPROM addresses shadow-copied to APROM at hardware init. APROM accessible at first 16 bytes of PCI IO space. */ I64 i; U64 eeprom_bytes = InU32(PCNetIOBaseGet) | InU32(PCNetIOBaseGet + 4) << 32; NetLog("PCNET GET MAC: Getting VM MAC."); for (i = 0; i < 6; i++) { pcnet.mac_address[i] = eeprom_bytes.u8[i]; NetLog(" %02X", pcnet.mac_address[i]); } } U0 PCNetDescriptorEntryInit(CPCNetDescriptorEntry *entry, U32 buffer_address, I64 is_rx) { U16 buffer_byte_cnt; entry->buffer_addr = buffer_address; /* AMD PCNet datasheet p.1-991. BCNT is the usable buffer length, expressed as first 12 bits of 2s-complement of desired length. Bits 0-11 of a DE are for the buffer byte cnt (BCNT), and bits 12-15 of a DE must be written all ones (ONES) */ buffer_byte_cnt = -ETHERNET_FRAME_SIZE; // Sets up as 2s complement of the desired length. buffer_byte_cnt &= 0x0FFF; // Masks 0 over everything except bits 0-11. entry->status1 |= buffer_byte_cnt; // Sets BCNT reg (first 12 bits) in DE TMD1/RMD1. entry->status1 |= 0xF000; // Sets bits 12-15 (ONES) in DE TMD1/RMD1 as all ones. //if this is a Receive DE, give ownership to the card so the PCNet can fill them. if (is_rx) PCIBts(&entry->status1, PCNET_DESCRIPTORf_OWN); } U0 PCNetBuffersAllocate() { I64 de_index; // used in for loops for TX and RX DE access. /* AMD PCNet datasheet p.1-913, p.1-990 When SSIZE32=1, Descriptor Ring Entry Base Address must be on 16-byte boundary. (TDRA[3:0]=0, RDRA[3:0]=0) */ pcnet.rx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_RX_BUFF_COUNT, 16, Fs->code_heap); pcnet.tx_de_buffer_phys = CAllocAligned(sizeof(CPCNetDescriptorEntry) * PCNET_TX_BUFF_COUNT, 16, Fs->code_heap); //Shrine does a check and returns -1 here, if the end of either buffer exceeds 0x100000000 pcnet.rx_de_buffer = dev.uncached_alias + pcnet.rx_de_buffer_phys; // we want uncached pcnet.tx_de_buffer = dev.uncached_alias + pcnet.tx_de_buffer_phys; // access to these. pcnet.rx_buffer_addr_phys = CAllocAligned(ETHERNET_FRAME_SIZE * PCNET_RX_BUFF_COUNT, 16, Fs->code_heap); pcnet.tx_buffer_addr_phys = CAllocAligned(ETHERNET_FRAME_SIZE * PCNET_TX_BUFF_COUNT, 16, Fs->code_heap); //Shrine does a check and returns -1 here, if the end of either buffer exceeds 0x100000000 CPCNetDescriptorEntry *entry = pcnet.rx_de_buffer; for (de_index = 0; de_index < PCNET_RX_BUFF_COUNT; de_index++) { PCNetDescriptorEntryInit(&entry[de_index], pcnet.rx_buffer_addr_phys + de_index * ETHERNET_FRAME_SIZE, TRUE); // TRUE for is_rx. } entry = pcnet.tx_de_buffer; for (de_index = 0; de_index < PCNET_TX_BUFF_COUNT; de_index++) { PCNetDescriptorEntryInit(&entry[de_index], pcnet.tx_buffer_addr_phys + de_index * ETHERNET_FRAME_SIZE, FALSE); // FALSE for is_rx. } } /* U0 PCNetDirectInit() {/* AMD PCNet datasheet p. 1-1021 Instead of setting up initialization block, direct writes to the necessary CSRs can be used to manually initialize the PCNet card. */ /* AMD PCNet datasheet p.1-991 If Logical Address Filter is set as all 0, all incoming logical addresses are rejected. Disables multicast. */ PCNetCSRWrite(PCNET_CSR_LADRF0, 0); PCNetCSRWrite(PCNET_CSR_LADRF1, 0); PCNetCSRWrite(PCNET_CSR_LADRF2, 0); PCNetCSRWrite(PCNET_CSR_LADRF3, 0); /* The Physical Address is the MAC. AMD PCNet datasheet p.1-960, 1-961 The first 16 bits of CSRs 12-14 are for the Physical Address, the upper bits are reserved, written 0 read undefined. The OR and bit-shift of 8 allows writing separate U8 values in the correct locations of the CSR. */ NetLog("PCNetDirectInit: Write MAC to CSR: 0x%X ", pcnet.mac_address[0] | (pcnet.mac_address[1] << 8)); PCNetCSRWrite(PCNET_CSR_PADR0, pcnet.mac_address[0] | (pcnet.mac_address[1] << 8)); NetLog("PCNetDirectInit: Write MAC to CSR: 0x%X ", pcnet.mac_address[2] | (pcnet.mac_address[3] << 8)); PCNetCSRWrite(PCNET_CSR_PADR1, pcnet.mac_address[2] | (pcnet.mac_address[3] << 8)); NetLog("PCNetDirectInit: Write MAC to CSR: 0x%X ", pcnet.mac_address[4] | (pcnet.mac_address[5] << 8)); PCNetCSRWrite(PCNET_CSR_PADR2, pcnet.mac_address[4] | (pcnet.mac_address[5] << 8)); /* AMD PCNet datasheet p.1-961, 1-962, 1-963 Refer to datasheet for specifics. Most relevant, when setting Mode to 0, promiscuous mode is is disabled, TX and RX enabled, enable RX broadcast and unicast. */ PCNetCSRWrite(PCNET_CSR_MODE, 0); /* AMD PCNet datasheet p.1-964 CSR 24 and 25 need to be filled with the lower and upper 16 bits, respectively, of the address of the RX packet ring. Likewise for CSR 30 and 31 for the TX packet ring. 0xFFFF AND on address will leave only lower 16 bits remaining. Bitshift right of 16 will replace first 16 bits with upper 16 bits, remaining bits cleared.*/ PCNetCSRWrite(PCNET_CSR_BADRL, pcnet.rx_buffer_addr_phys & 0xFFFF); PCNetCSRWrite(PCNET_CSR_BADRU, pcnet.rx_buffer_addr_phys >> 16); PCNetCSRWrite(PCNET_CSR_BADTL, pcnet.tx_buffer_addr_phys & 0xFFFF); PCNetCSRWrite(PCNET_CSR_BADTU, pcnet.tx_buffer_addr_phys >> 16); /* AMD PCNet datasheet p. 1-967 Dft value at hardware init is all 0. Standard init block process sets this, but if doing directly it is imperative to manually set it 0. */ PCNetCSRWrite(PCNET_CSR_POLLINT, 0); /* AMD PCNet datasheet p. 1-970 Receive and Transmit Ring Length CSRs bits 0-15 need to be set as the 2s complement of the ring length. The AND with 0xFFFF clears the upper Reserved bits, which are to be written as zeroes read undefined. */ PCNetCSRWrite(PCNET_CSR_RXRINGLEN, -PCNET_RX_BUFF_COUNT & 0xFFFF); PCNetCSRWrite(PCNET_CSR_TXRINGLEN, -PCNET_TX_BUFF_COUNT & 0xFFFF); } */ U8 *PCNetInitBlockSetup() { U8 *setup = CAllocAligned(sizeof(CPCNetBufferSetup), 16, Fs->code_heap); CPCNetBufferSetup *u_setup = setup + dev.uncached_alias; U32 p_setup; u_setup->mode = 0; u_setup->rlen = 6 << 4; u_setup->tlen = 6 << 4; MemCpy(u_setup->mac, pcnet.mac_address, 6); u_setup->reserved = 0; MemSet(u_setup->ladr, 0, 8); u_setup->rxbuf = pcnet.rx_de_buffer_phys; u_setup->txbuf = pcnet.tx_de_buffer_phys; p_setup = setup; PCNetCSRWrite(1, p_setup & 0xFFFF); PCNetCSRWrite(2, p_setup >> 16); return setup; } U0 PCNetInterruptCSRSet() { /* AMD PCNet datasheet p.1-952, 1-953, 1-954, 1-955, 1-956, 1-957 Refer to datasheet for specifics on the Interrupt Masks. Most of these, when set 0, allow interrupts to be set in CSR0. We set Big-Endian disabled, RX interrupts enabled, Init Done interrupt disabled, and TX interrupt disabled. */ U32 csr = PCNetCSRRead(PCNET_CSR_INTERRUPTS); Btr(&csr, PCNET_INT_BSWP); Btr(&csr, PCNET_INT_RINTM); Bts(&csr, PCNET_INT_IDONM); Bts(&csr, PCNET_INT_TINTM); PCNetCSRWrite(PCNET_CSR_INTERRUPTS, csr); } U0 PCNetTXAutoPadEnable() { /* AMD PCNet datasheet p.1-958 Setting bit 11 (Auto Pad Transmit) allows shoft transmit frames to be automatically extended to 64 bytes. */ U32 csr = PCNetCSRRead(PCNET_CSR_FEATURECTRL); Bts(&csr, PCNET_FEATURE_APADXMT); PCNetCSRWrite(PCNET_CSR_FEATURECTRL, csr); } U0 PCNetCfgModeExit() { /* AMD PCNet datasheet p.1-954 PCNet controller can be started after configuring by ensuring INIT and STOP are cleared and START bit is set, in Status and Control Register (CSR0). */ U32 csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); Btr(&csr, PCNET_CTRL_INIT); Btr(&csr, PCNET_CTRL_STOP); Bts(&csr, PCNET_CTRL_IENA); Bts(&csr, PCNET_CTRL_STRT); PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr); } U0 PCNetUploadCfg() { /* Upload new config and wait for card to acknowlege */ U32 csr = 0; Bts(&csr, PCNET_CTRL_INIT); PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr); Btr(&csr, PCNET_CTRL_IDON); while (!Bt(&csr, PCNET_CTRL_IDON)) { Yield; csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); } } I64 PCNetDriverOwns(CPCNetDescriptorEntry* entry) { /* Returns whether the value of the OWN bit of the Descriptor Entry is zero. If 0, driver owns, if 1, PCNet card owns it. */ return !PCIBt(&entry->status1, PCNET_DESCRIPTORf_OWN); } I64 PCNetTransmitPacketAllocate(U8 **packet_buffer_out, I64 length) { /* Transmits the packet at the current TX DE index. The packet_buffer_out is a pointer, since we modify its value, ending with returning the index of the DE we just processed. Length is validated to fit in BCNT. The increment of the current TX DE index is done by assigning it the value of incrementing it AND the max DE index-1. This will increment it as well as wrap back to 0 if we hit the max DE index. */ U16 buffer_byte_cnt; I64 de_index = pcnet.current_tx_de_index; if (length > 0xFFF) { // Max packet length must fit into BCNT 12-bit register. NetErr("PCNET ALLOCATE TX PACKET: Invalid TX Packet Length"); throw('PCNet'); } CPCNetDescriptorEntry *entry = &pcnet.tx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)]; if (!PCNetDriverOwns(entry)) { NetErr("PCNET ALLOCATE TX PACKET: TX FIFO Full"); return -1; // Positive value expected. Functions calling this must factor this in. } else { NetLog("PCNET ALLOCATE TX PACKET: Driver owns TX DE at index %d.", de_index); } PCIBts(&entry->status1, PCNET_DESCRIPTORf_STP); PCIBts(&entry->status1, PCNET_DESCRIPTORf_ENP); /* AMD PCNet datasheet p.1-991. BCNT is the usable buffer length, expressed as first 12 bits of 2s-complement of desired length. Bits 0-11 of a DE are for the buffer byte cnt (BCNT), and bits 12-15 of a DE must be written all ones (ONES) */ buffer_byte_cnt = -length; // Sets up as 2s complement of the desired length. buffer_byte_cnt &= 0x0FFF; // Masks 0 over everything except bits 0-11. entry->status1 &= 0xFFFFF000; // Clear first 12 bits and retain other bits in DE TMD1. entry->status1 |= buffer_byte_cnt; // Sets BCNT reg (first 12 bits) in DE TMD1. entry->status1 |= 0xF000; // Sets bits 12-15 (ONES) in DE TMD1 as all ones. pcnet.current_tx_de_index = (pcnet.current_tx_de_index + 1) & (PCNET_TX_BUFF_COUNT - 1); *packet_buffer_out = pcnet.tx_buffer_addr_phys + de_index * ETHERNET_FRAME_SIZE; NetLog("PCNET ALLOCATE TX PACKET: de_index: %X.", de_index); return de_index; } U0 PCNetTransmitPacketFinish(I64 de_index) { /* Release ownership of the packet to the PCNet card by setting the OWN bit to 1. */ CPCNetDescriptorEntry *entry = &pcnet.tx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)]; PCIBts(&entry->status1, PCNET_DESCRIPTORf_OWN); NetLog("PCNET FINISH TX PACKET: TX DE index: %X, OWN bit of entry at entry: %b.", de_index, PCIBt(&entry->status1, PCNET_DESCRIPTORf_OWN)); } U0 EthernetFrameFinish(I64 de_index) {//Alias for driver Finish TX function. PCNetTransmitPacketFinish(de_index); } I64 PCNetPacketReceive(U8 **packet_buffer_out, U16 *packet_length_out) { /* Receives the packet at the current RX DE index. Parameters are both pointers, since we modify the value at the packet_buffer_out, and at the packet_length, ending with returning the index of the DE we just processed. The MCNT is stored at the first two bytes of the RMD2. We AND with 0xFFFF to only take in those first two bytes: that is the packet_length. The increment of the current RX DE index is done by assigning it the value of incrementing it AND the max DE index-1. This will increment it as well as wrap back to 0 if we hit the max DE index. */ I64 de_index = pcnet.current_rx_de_index; U16 packet_length; NetLog("PCNET RECEIVE PACKET: Output buffer ptr, output length ptr: %X, %X ", packet_buffer_out, packet_length_out); CPCNetDescriptorEntry *entry = &pcnet.rx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)]; packet_length = entry->status2 & 0xFFFF; NetDbg("PCNET RECEIVE PACKET: de_index = 0x%0X", de_index); pcnet.current_rx_de_index = (pcnet.current_rx_de_index + 1) & (PCNET_RX_BUFF_COUNT - 1); NetDbg("PCNET RECEIVE PACKET: de_index incremented = 0x%0X", pcnet.current_rx_de_index); *packet_buffer_out = pcnet.rx_buffer_addr_phys + de_index * ETHERNET_FRAME_SIZE; *packet_length_out = packet_length; return de_index; } U0 PCNetReceivePacketRelease(I64 de_index) { /* Release ownership of the packet to the PCNet card by setting the OWN bit to 1. */ CPCNetDescriptorEntry *entry = &pcnet.rx_de_buffer[de_index * sizeof(CPCNetDescriptorEntry)]; PCIBts(&entry->status1, PCNET_DESCRIPTORf_OWN); } interrupt U0 PCNetIRQ() {// todo: comments explaining process...maybe reimplement interrupt handling altogether. U8 *packet_buffer; U16 packet_length; I64 de_index; U32 csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); // "Interrupt Reason: %X , %b\n",csr,csr;Dbg; CPCNetDescriptorEntry *entry = pcnet.rx_de_buffer; while (PCNetDriverOwns(&entry[pcnet.current_rx_de_index])) { if (PCNET_DEBUG_OUTPUT) { NetLog("$BG,LTCYAN$$FG,WHITE$" "==== PCNET IRQ ====" "$BG$$FG$"); NetLog("$BD,CYAN$$FD,WHITE$" "PCNET IRQ: Saw owned RX DE index %d.", pcnet.current_rx_de_index); } de_index = PCNetPacketReceive(&packet_buffer, &packet_length); if (de_index >= 0) // todo: necessary? check increment logic in PCNetPacketReceive. { if (PCNET_DEBUG_OUTPUT) { NetLog("PCNET IRQ: Pushing copy into Net Que, releasing receive packet."); } // uncached read NetQuePush(packet_buffer + dev.uncached_alias, packet_length); PCNetReceivePacketRelease(de_index); } Bts(&csr, PCNET_CTRL_RINT); PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr); if (PCNET_DEBUG_OUTPUT) { NetLog("PCNET IRQ: Exiting.\n" "$BD,WHITE$$FD,LTGRAY$" "$BG,LTCYAN$$FG,WHITE$" "===================" "$BG$$FG$"); } } *(dev.uncached_alias + LAPIC_EOI)(U32*) = 0; } U0 PCIInterruptsReroute(I64 base) {// todo: comments explaining process, maybe better var names I64 i; U8 *da = dev.uncached_alias + IOAPIC_REG; U32 *_d = dev.uncached_alias + IOAPIC_DATA; for (i = 0; i < 4; i++) { *da = IOREDTAB + i * 2 + 1; *_d = dev.mp_apic_ids[INT_DEST_CPU] << 24; *da = IOREDTAB + i * 2; *_d = 0x4000 + base + i; } } U0 PCNetInterruptsSetup() {// todo: comments explaining process I64 i; for (i = 0; i < 4; i++) IntEntrySet(0x40+i, &PCNetIRQ); PciRerouteInterrupts(0x40); } U0 PCNetInit() { U8 *setup; // PCNetInitBlockSetup MemSet(&pcnet, 0, sizeof(CPCNet)); // pcnet global var will hold member data the driver uses often. pcnet.pci = &net_driver_pci; if (!pcnet.pci) return; // if we don't find the card, quit. /* Clear command register of PCNet PCI device, set IO Enable and Bus Master Enable bits of the register. */ PCIWriteU16(pcnet.pci->bus, pcnet.pci->dev, pcnet.pci->fun, PCIR_COMMAND, PCNET_CMDF_IOEN | PCNET_CMDF_BMEN); PCNetRst; PCNet32BitModeEnable; U32 csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); NetLog("PCNET INIT START: what is INIT ?: %d", Bt(&csr, PCNET_CTRL_INIT)); NetLog("PCNET INIT START: what is STRT ?: %d", Bt(&csr, PCNET_CTRL_STRT)); NetLog("PCNET INIT START: what is STOP ?: %d", Bt(&csr, PCNET_CTRL_STOP)); NetLog("PCNET INIT START: what is RINT ?: %d", Bt(&csr, PCNET_CTRL_RINT)); PCNetSWStyleSet; PCNetMACGet; // OSDev has code ensuring auto selected connection... PCNetBuffersAllocate; // PCNetDirectInit; setup = PCNetInitBlockSetup(); PCNetInterruptCSRSet; PCNetTXAutoPadEnable; // Reg 2 Miscellaneous Configuration bits ASEL (this register should default to 2) U32 bcr2=PCNetBCRRead(2); bcr2 |= 2; PCNetBCRWrite(2,bcr2); // Reg 9 ull-Duplex Control. (DEFAULT= 0000) Set full duplex bcr2 = PCNetBCRRead(9) & ~3; bcr2 |= 3; // full duplex enable, aui full duplex PCNetBCRWrite(9,bcr2); /* // Reg 32 - MII Status and Control (DEFAULT = 0400) - Enable auto negotiate PCNetCSRWrite(32,PCNetCSRRead(32)|0x0080); bcr2=PCNetCSRRead(32) & ~0x98; bcr2 |= 0x20; // XPHYANE PCNetCSRWrite(32,bcr2); */ PCNetUploadCfg; csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); NetLog("PCNET INIT UPLOAD: what is INIT ?: %d", Bt(&csr, PCNET_CTRL_INIT)); NetLog("PCNET INIT UPLOAD: what is STRT ?: %d", Bt(&csr, PCNET_CTRL_STRT)); NetLog("PCNET INIT UPLOAD: what is STOP ?: %d", Bt(&csr, PCNET_CTRL_STOP)); NetLog("PCNET INIT UPLOAD: what is RINT ?: %d", Bt(&csr, PCNET_CTRL_RINT)); PCNetCfgModeExit; Sleep(100); //? necessary? csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); NetLog("PCNET INIT END: what is INIT ?: %d", Bt(&csr, PCNET_CTRL_INIT)); NetLog("PCNET INIT END: what is STRT ?: %d", Bt(&csr, PCNET_CTRL_STRT)); NetLog("PCNET INIT END: what is STOP ?: %d", Bt(&csr, PCNET_CTRL_STOP)); NetLog("PCNET INIT END: what is RINT ?: %d", Bt(&csr, PCNET_CTRL_RINT)); NetLog("PCNET INIT END: what is TXON ?: %d", Bt(&csr, 4)); NetLog("PCNET INIT END: what is RXON ?: %d", Bt(&csr, 5)); csr = PCNetCSRRead(PCNET_CSR_POLLINT); NetLog("PCNET INIT END: what is POLLINT ?: %d", Bt(&csr, PCNET_CTRL_RINT)); NetLog("PCNET INIT END: Redirecting interrupts."); PCNetInterruptsSetup; Free(setup); } I64 EthernetFrameAllocate(U8 **packet_buffer_out, U8 *source_address, U8 *destination_address, U16 ethertype, I64 packet_length) { /* Allocate an Ethernet Frame for transmit. The source and destination addresses are copied to the Frame, as well as the ethertype. The packet_buffer_out parameter has the value at its pointer set to the payload of the Ethernet Frame. */ U8 *ethernet_frame; I64 de_index; //need to see if 3 years later VirtualBox supports APAD_XMT! if (packet_length < ETHERNET_MIN_FRAME_SIZE) { NetWarn("ETHERNET FRAME ALLOCATE: PCNET APAD XMT TRUNCATE ? ..."); packet_length = ETHERNET_MIN_FRAME_SIZE; } de_index = PCNetTransmitPacketAllocate(&ethernet_frame, ETHERNET_MAC_HEADER_LENGTH + packet_length); ethernet_frame += dev.uncached_alias; // Mk write uncached if (de_index < 0) { NetErr("ETHERNET FRAME ALLOCATE: Failure"); return -1; // Positive value expected. Functions calling this must factor this in. } MemSet(ethernet_frame, 0, ETHERNET_FRAME_SIZE); // Clear buffer contents in advance. MemCpy(ethernet_frame, destination_address, MAC_ADDRESS_LENGTH); MemCpy(ethernet_frame + MAC_ADDRESS_LENGTH, source_address, MAC_ADDRESS_LENGTH); ethernet_frame[ETHERNET_ETHERTYPE_OFFSET] = ethertype >> 8; ethernet_frame[ETHERNET_ETHERTYPE_OFFSET + 1] = ethertype & 0xFF; *packet_buffer_out = ethernet_frame + ETHERNET_MAC_HEADER_LENGTH; return de_index; } U8 *EthernetMACGet() { return pcnet.mac_address; } U0 NetStop() {// Halt network activity by setting STOP bit on Status CSR. U32 csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); Bts(&csr, PCNET_CTRL_STOP); PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr); } U0 NetStart() {// Continue network activity. Setting START bit clears STOP/INIT. U32 csr = PCNetCSRRead(PCNET_CSR_CTRLSTATUS); Bts(&csr, PCNET_CTRL_STRT); PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr); } PCNetInit;