mirror of
https://github.com/Zeal-Operating-System/ZealOS.git
synced 2025-06-07 08:14:48 +00:00
835 lines
25 KiB
Plaintext
Executable File
835 lines
25 KiB
Plaintext
Executable File
/* 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_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_BCR_MISC_CONFIG 2
|
||
#define PCNET_BCR_FULL_DUPLEX_CTRL 9
|
||
|
||
#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_BCR_MISC_CONFIG_ASEL 1
|
||
|
||
#define PCNET_BCR_FULL_DUPLEX_CTRL_FDEN 0
|
||
#define PCNET_BCR_FULL_DUPLEX_CTRL_AUIFD 1
|
||
|
||
#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 32 // Linux & Shrine Driver use 32 and 8 for
|
||
#define PCNET_TX_BUFF_COUNT 8 // these, we could allow more if wanted.
|
||
|
||
#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
|
||
{
|
||
CPCIDev *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->base[0] & ~0x1F;
|
||
return io_base;
|
||
}
|
||
|
||
U0 PCNetReset()
|
||
{/* 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 <20>S
|
||
}
|
||
|
||
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 PCNetAutoLinkSelect()
|
||
{
|
||
U32 bcr = PCNetCSRRead(PCNET_BCR_FULL_DUPLEX_CTRL);
|
||
|
||
Bts(&bcr, PCNET_BCR_FULL_DUPLEX_CTRL_FDEN);
|
||
Bts(&bcr, PCNET_BCR_FULL_DUPLEX_CTRL_AUIFD);
|
||
|
||
PCNetBCRWrite(PCNET_BCR_FULL_DUPLEX_CTRL, bcr);
|
||
}
|
||
|
||
U0 PCNetEnableFullDuplex()
|
||
{
|
||
U32 bcr = PCNetCSRRead(PCNET_BCR_MISC_CONFIG);
|
||
|
||
Bts(&bcr, PCNET_BCR_MISC_CONFIG_ASEL);
|
||
|
||
PCNetBCRWrite(PCNET_BCR_MISC_CONFIG, bcr);
|
||
}
|
||
|
||
|
||
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_count;
|
||
|
||
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 count (BCNT),
|
||
and bits 12-15 of a DE must be written all ones (ONES) */
|
||
|
||
buffer_byte_count = -ETHERNET_FRAME_SIZE; // Sets up as 2s complement of the desired length.
|
||
buffer_byte_count &= 0x0FFF; // Masks 0 over everything except bits 0-11.
|
||
|
||
entry->status1 |= buffer_byte_count; // 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
|
||
Default 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 = 5 << 4;
|
||
u_setup->tlen = 3 << 4;
|
||
MemCopy(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 PCNetConfigModeExit()
|
||
{/* 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 PCNetUploadConfig()
|
||
{/* 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_count;
|
||
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 count (BCNT),
|
||
and bits 12-15 of a DE must be written all ones (ONES) */
|
||
buffer_byte_count = -length; // Sets up as 2s complement of the desired length.
|
||
buffer_byte_count &= 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_count; // 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;
|
||
|
||
NetDebug("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);
|
||
NetDebug("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;Debug;
|
||
|
||
CPCNetDescriptorEntry *entry = pcnet.rx_de_buffer;
|
||
|
||
while (PCNetDriverOwns(&entry[pcnet.current_rx_de_index]))
|
||
{
|
||
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.
|
||
{
|
||
NetLog("PCNET IRQ: Pushing copy into Net Queue, releasing receive packet.");
|
||
// uncached read
|
||
NetQueuePush(packet_buffer + dev.uncached_alias, packet_length);
|
||
PCNetReceivePacketRelease(de_index);
|
||
}
|
||
|
||
Bts(&csr, PCNET_CTRL_RINT);
|
||
|
||
PCNetCSRWrite(PCNET_CSR_CTRLSTATUS, csr);
|
||
|
||
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 irq, i;
|
||
|
||
for (i = 0; i < 4; i++)
|
||
IntEntrySet((irq = IntEntryAlloc), &PCNetIRQ);
|
||
|
||
PCIInterruptsReroute(irq);
|
||
}
|
||
|
||
U0 PCNetInit()
|
||
{
|
||
U8 *setup; // PCNetInitBlockSetup
|
||
|
||
MemSet(&pcnet, 0, sizeof(CPCNet)); // pcnet global var will hold member data the driver uses often.
|
||
|
||
pcnet.pci = PCNetPCIDevFind;
|
||
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);
|
||
|
||
PCNetReset;
|
||
|
||
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;
|
||
|
||
PCNetAutoLinkSelect;
|
||
|
||
PCNetEnableFullDuplex;
|
||
|
||
PCNetUploadConfig;
|
||
|
||
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));
|
||
|
||
PCNetConfigModeExit;
|
||
|
||
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(ðernet_frame, ETHERNET_MAC_HEADER_LENGTH + packet_length);
|
||
ethernet_frame += dev.uncached_alias; // Make 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.
|
||
|
||
MemCopy(ethernet_frame, destination_address, MAC_ADDRESS_LENGTH);
|
||
MemCopy(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; |