Implement 3C509B (EtherLink III) ISA Ethernet NIC driver (AI)
- Created src/e3c509.h: full windowed register model (8 windows), command codes, status/interrupt bits, RX filter, transceiver types, device struct and API declarations - Created src/e3c509.c: full driver implementation with PIO TX/RX, window selection, 10base-T (RJ45) transceiver config, MAC address read from Window 2, FIFO-based packet send/receive, IRQ handler, devicefs char device registration as 'eth' class - Probe uses manufacturer ID check (0x6D50 at Window 0) - Only 10base-T supported per design requirements - Wired IRQ 10 (vector 42) handler into isr.c - QEMU does not emulate 3C509 ISA, so driver correctly probes 'not found' in QEMU; tested alongside NE2000 without issues
This commit is contained in:
@@ -68,8 +68,8 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Add support for character device to the devicefs subsystem.
|
||||
- [x] Create an app called `diskpart`. This app can be used to modify the MBR partitions on a block device.
|
||||
- [x] Create an app called `mkfs.fat32`. This app can be used to format a block into a FAT32 filesystem.
|
||||
- [ ] Create a network driver for the NE2000 NIC.
|
||||
- [ ] Create a network driver for the 3C509B NIC. It should only support RJ45 and 10base-T.
|
||||
- [x] Create a network driver for the NE2000 NIC.
|
||||
- [x] Create a network driver for the 3C509B NIC. It should only support RJ45 and 10base-T.
|
||||
- [ ] Create an ethernet subsytsem. Each ethernet device should be shown as a character device with the name `ethN`.
|
||||
- [ ] Create a IPv4 stack. Create the `ip` app that shows curernt IPv4 configuration. It should read this information from `/sys`
|
||||
- [ ] Create a ARP subsystem. Create the `arp` command that shows current ARP tables. Again, this info should be found in `/sys`
|
||||
|
||||
@@ -26,6 +26,7 @@ add_executable(kernel
|
||||
fat32.c
|
||||
floppy.c
|
||||
ne2000.c
|
||||
e3c509.c
|
||||
env.c
|
||||
keyboard.c
|
||||
interrupts.S
|
||||
|
||||
427
src/e3c509.c
Normal file
427
src/e3c509.c
Normal file
@@ -0,0 +1,427 @@
|
||||
/**
|
||||
* @file e3c509.c
|
||||
* @brief 3Com 3C509B (EtherLink III) ISA Ethernet NIC driver.
|
||||
*
|
||||
* Drives 3Com 3C509/3C509B Ethernet adapters using PIO (programmed I/O).
|
||||
* The 3C509B uses a windowed register model: 8 register windows of 16 I/O
|
||||
* ports each, selected by writing to the command register.
|
||||
*
|
||||
* Only 10base-T (RJ45) operation is supported, per design requirements.
|
||||
*
|
||||
* Packet TX: write packet length, then write data to TX PIO port.
|
||||
* Packet RX: poll RX status, read data from RX PIO port.
|
||||
*/
|
||||
|
||||
#include "e3c509.h"
|
||||
#include "port_io.h"
|
||||
#include "pic.h"
|
||||
#include "devicefs.h"
|
||||
#include "driver.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Debug print helpers */
|
||||
extern void offset_print(const char *str);
|
||||
extern void print_hex(uint32_t val);
|
||||
|
||||
/* ================================================================
|
||||
* Global state
|
||||
* ================================================================ */
|
||||
|
||||
/** Single 3C509B device. */
|
||||
static e3c509_device_t e3c509_dev;
|
||||
|
||||
/** Volatile flags set by IRQ handler. */
|
||||
static volatile int e3c509_rx_ready = 0;
|
||||
static volatile int e3c509_tx_done = 0;
|
||||
|
||||
/* ================================================================
|
||||
* Register access helpers
|
||||
* ================================================================ */
|
||||
|
||||
/** Write a 16-bit command to the 3C509B command register. */
|
||||
static inline void e3c509_cmd(uint16_t base, uint16_t cmd) {
|
||||
outw(base + E3C509_CMD, cmd);
|
||||
}
|
||||
|
||||
/** Read the 16-bit status register. */
|
||||
static inline uint16_t e3c509_status(uint16_t base) {
|
||||
return inw(base + E3C509_STATUS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a register window.
|
||||
*/
|
||||
static void e3c509_select_window(uint16_t base, uint16_t window) {
|
||||
e3c509_cmd(base, CMD_SELECT_WINDOW | (window & 0x07));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a command to complete.
|
||||
*/
|
||||
static void e3c509_wait_cmd(uint16_t base) {
|
||||
int timeout = 100000;
|
||||
while ((e3c509_status(base) & STAT_CMD_IN_PROG) && --timeout > 0);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Packet send / receive
|
||||
* ================================================================ */
|
||||
|
||||
int e3c509_send(e3c509_device_t *dev, const void *data, uint32_t len) {
|
||||
if (!dev || !dev->present) return -1;
|
||||
if (len > ETH_FRAME_MAX) return -1;
|
||||
|
||||
uint16_t base = dev->io_base;
|
||||
uint32_t send_len = len;
|
||||
if (send_len < 60) send_len = 60;
|
||||
|
||||
/* Switch to window 1 */
|
||||
e3c509_select_window(base, 1);
|
||||
|
||||
/* Wait for free TX space */
|
||||
int timeout = 100000;
|
||||
while (inw(base + E3C509_W1_FREE_TX) < send_len + 4 && --timeout > 0) {
|
||||
asm volatile("pause");
|
||||
}
|
||||
if (timeout == 0) {
|
||||
offset_print(" 3C509: tx fifo full\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Write TX preamble: packet length (low 11 bits, bit 15 = no interrupt) */
|
||||
outw(base + E3C509_W1_TX_PIO, (uint16_t)(send_len | 0x00));
|
||||
|
||||
/* Pad to dword-aligned length */
|
||||
outw(base + E3C509_W1_TX_PIO, 0x0000);
|
||||
|
||||
/* Write packet data as 16-bit words */
|
||||
const uint16_t *data16 = (const uint16_t *)data;
|
||||
uint32_t words = len / 2;
|
||||
for (uint32_t i = 0; i < words; i++) {
|
||||
outw(base + E3C509_W1_TX_PIO, data16[i]);
|
||||
}
|
||||
/* Write last byte if odd */
|
||||
if (len & 1) {
|
||||
const uint8_t *data8 = (const uint8_t *)data;
|
||||
outw(base + E3C509_W1_TX_PIO, (uint16_t)data8[len - 1]);
|
||||
}
|
||||
|
||||
/* Pad to minimum frame size with zeros */
|
||||
if (len < 60) {
|
||||
uint32_t pad_words = (60 - len + 1) / 2;
|
||||
for (uint32_t i = 0; i < pad_words; i++) {
|
||||
outw(base + E3C509_W1_TX_PIO, 0x0000);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for TX complete */
|
||||
e3c509_tx_done = 0;
|
||||
timeout = 1000000;
|
||||
while (!e3c509_tx_done && --timeout > 0) {
|
||||
/* Check TX status directly in case interrupts are slow */
|
||||
uint8_t txstat = inb(base + E3C509_W1_TX_STATUS);
|
||||
if (txstat & 0x80) { /* TX complete */
|
||||
/* Acknowledge by writing status back */
|
||||
outb(base + E3C509_W1_TX_STATUS, txstat);
|
||||
break;
|
||||
}
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e3c509_recv(e3c509_device_t *dev, void *buf, uint32_t bufsize) {
|
||||
if (!dev || !dev->present) return -1;
|
||||
|
||||
uint16_t base = dev->io_base;
|
||||
|
||||
/* Switch to window 1 */
|
||||
e3c509_select_window(base, 1);
|
||||
|
||||
/* Read RX status */
|
||||
uint16_t rx_status = inw(base + E3C509_W1_RX_STATUS);
|
||||
|
||||
/* Check if a packet is available (bit 15 = incomplete/error) */
|
||||
if (rx_status & 0x8000) {
|
||||
/* Error — discard the packet */
|
||||
e3c509_cmd(base, CMD_RX_DISCARD);
|
||||
e3c509_wait_cmd(base);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Bits 10:0 = packet length */
|
||||
uint16_t pkt_len = rx_status & 0x07FF;
|
||||
if (pkt_len == 0) {
|
||||
return 0; /* No packet */
|
||||
}
|
||||
|
||||
uint32_t copy_len = pkt_len;
|
||||
if (copy_len > bufsize) copy_len = bufsize;
|
||||
|
||||
/* Read packet data as 16-bit words */
|
||||
uint16_t *buf16 = (uint16_t *)buf;
|
||||
uint32_t read_words = copy_len / 2;
|
||||
for (uint32_t i = 0; i < read_words; i++) {
|
||||
buf16[i] = inw(base + E3C509_W1_RX_PIO);
|
||||
}
|
||||
if (copy_len & 1) {
|
||||
uint16_t w = inw(base + E3C509_W1_RX_PIO);
|
||||
((uint8_t *)buf)[copy_len - 1] = (uint8_t)(w & 0xFF);
|
||||
}
|
||||
|
||||
/* Discard any remaining data and advance */
|
||||
e3c509_cmd(base, CMD_RX_DISCARD);
|
||||
e3c509_wait_cmd(base);
|
||||
|
||||
return (int)copy_len;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* IRQ handler
|
||||
* ================================================================ */
|
||||
|
||||
void e3c509_irq(void) {
|
||||
if (!e3c509_dev.present) return;
|
||||
|
||||
uint16_t base = e3c509_dev.io_base;
|
||||
uint16_t status = e3c509_status(base);
|
||||
|
||||
if (status & STAT_RX_COMPLETE) {
|
||||
e3c509_rx_ready = 1;
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_RX_COMPLETE);
|
||||
}
|
||||
|
||||
if (status & STAT_TX_COMPLETE) {
|
||||
e3c509_tx_done = 1;
|
||||
/* Read and clear TX status */
|
||||
e3c509_select_window(base, 1);
|
||||
uint8_t txstat = inb(base + E3C509_W1_TX_STATUS);
|
||||
outb(base + E3C509_W1_TX_STATUS, txstat);
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_TX_COMPLETE);
|
||||
}
|
||||
|
||||
if (status & STAT_TX_AVAILABLE) {
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_TX_AVAILABLE);
|
||||
}
|
||||
|
||||
if (status & STAT_ADAPTER_FAIL) {
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_ADAPTER_FAIL);
|
||||
}
|
||||
|
||||
if (status & STAT_UPDATE_STATS) {
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_UPDATE_STATS);
|
||||
}
|
||||
|
||||
/* Acknowledge the interrupt latch */
|
||||
e3c509_cmd(base, CMD_ACK_INTR | STAT_INT_LATCH);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Character device operations
|
||||
* ================================================================ */
|
||||
|
||||
static int32_t e3c509_char_read(void *dev_data, uint32_t size, void *buf) {
|
||||
e3c509_device_t *dev = (e3c509_device_t *)dev_data;
|
||||
return (int32_t)e3c509_recv(dev, buf, size);
|
||||
}
|
||||
|
||||
static int32_t e3c509_char_write(void *dev_data, uint32_t size, const void *buf) {
|
||||
e3c509_device_t *dev = (e3c509_device_t *)dev_data;
|
||||
if (e3c509_send(dev, buf, size) == 0) {
|
||||
return (int32_t)size;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static devicefs_char_ops_t e3c509_char_ops = {
|
||||
.read = e3c509_char_read,
|
||||
.write = e3c509_char_write,
|
||||
};
|
||||
|
||||
/* ================================================================
|
||||
* Initialization
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Read the MAC address from the 3C509B's EEPROM (Window 2).
|
||||
*/
|
||||
static void e3c509_read_mac(uint16_t base, uint8_t *mac) {
|
||||
e3c509_select_window(base, 2);
|
||||
uint16_t w0 = inw(base + E3C509_W2_ADDR0);
|
||||
uint16_t w1 = inw(base + E3C509_W2_ADDR1);
|
||||
uint16_t w2 = inw(base + E3C509_W2_ADDR2);
|
||||
|
||||
mac[0] = (uint8_t)(w0 & 0xFF);
|
||||
mac[1] = (uint8_t)((w0 >> 8) & 0xFF);
|
||||
mac[2] = (uint8_t)(w1 & 0xFF);
|
||||
mac[3] = (uint8_t)((w1 >> 8) & 0xFF);
|
||||
mac[4] = (uint8_t)(w2 & 0xFF);
|
||||
mac[5] = (uint8_t)((w2 >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the 3C509B hardware.
|
||||
*/
|
||||
static int e3c509_hw_init(uint16_t base) {
|
||||
/* Global reset */
|
||||
e3c509_cmd(base, CMD_GLOBAL_RESET);
|
||||
|
||||
/* Wait for reset to complete */
|
||||
int timeout = 100000;
|
||||
while ((e3c509_status(base) & STAT_CMD_IN_PROG) && --timeout > 0);
|
||||
if (timeout == 0) {
|
||||
offset_print(" 3C509: reset timeout\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Small extra delay */
|
||||
for (volatile int i = 0; i < 50000; i++) {
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
/* Read MAC address */
|
||||
e3c509_read_mac(base, e3c509_dev.mac);
|
||||
|
||||
/* Select 10base-T (RJ45) transceiver — Window 0, Address Config */
|
||||
e3c509_select_window(base, 0);
|
||||
uint16_t addr_cfg = inw(base + E3C509_W0_ADDR_CFG);
|
||||
/* Clear transceiver bits (14:13) and set to TP (00) */
|
||||
addr_cfg &= ~(0x3 << 14);
|
||||
addr_cfg |= (XCVR_TP << 14);
|
||||
outw(base + E3C509_W0_ADDR_CFG, addr_cfg);
|
||||
|
||||
/* Configure IRQ in resource config register */
|
||||
uint16_t res_cfg = inw(base + E3C509_W0_RES_CFG);
|
||||
/* IRQ is in bits 15:12. Set to our IRQ. */
|
||||
res_cfg = (res_cfg & 0x0FFF) | ((uint16_t)e3c509_dev.irq << 12);
|
||||
outw(base + E3C509_W0_RES_CFG, res_cfg);
|
||||
|
||||
/* Enable the adapter */
|
||||
outw(base + E3C509_W0_CFG_CTRL, 0x0001); /* Enable */
|
||||
|
||||
/* Reset TX and RX */
|
||||
e3c509_cmd(base, CMD_TX_RESET);
|
||||
e3c509_wait_cmd(base);
|
||||
e3c509_cmd(base, CMD_RX_RESET);
|
||||
e3c509_wait_cmd(base);
|
||||
|
||||
/* Set RX filter: accept station + broadcast */
|
||||
e3c509_cmd(base, CMD_SET_RX_FILTER | RX_FILTER_STATION | RX_FILTER_BCAST);
|
||||
|
||||
/* Set TX start threshold — start transmitting after full packet */
|
||||
e3c509_cmd(base, CMD_SET_TX_START | (ETH_FRAME_MAX >> 2));
|
||||
|
||||
/* Set interrupt mask */
|
||||
e3c509_cmd(base, CMD_SET_INTR_MASK |
|
||||
STAT_RX_COMPLETE | STAT_TX_COMPLETE | STAT_TX_AVAILABLE |
|
||||
STAT_ADAPTER_FAIL | STAT_UPDATE_STATS);
|
||||
|
||||
/* Enable TX and RX */
|
||||
e3c509_cmd(base, CMD_TX_ENABLE);
|
||||
e3c509_cmd(base, CMD_RX_ENABLE);
|
||||
|
||||
/* Acknowledge any pending interrupts */
|
||||
e3c509_cmd(base, CMD_ACK_INTR | 0xFF);
|
||||
|
||||
/* Switch to operating window (window 1) */
|
||||
e3c509_select_window(base, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Driver framework
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Probe for a 3C509B card.
|
||||
*
|
||||
* Check the manufacturer ID at Window 0, offset 0x00.
|
||||
* The 3Com 3C509B should return 0x6D50.
|
||||
*/
|
||||
static driver_probe_result_t e3c509_probe(void) {
|
||||
uint16_t base = E3C509_DEFAULT_IOBASE;
|
||||
|
||||
/* Try to select Window 0 and read manufacturer ID */
|
||||
e3c509_cmd(base, CMD_SELECT_WINDOW | 0);
|
||||
|
||||
/* Brief delay */
|
||||
for (volatile int i = 0; i < 10000; i++) {
|
||||
asm volatile("pause");
|
||||
}
|
||||
|
||||
uint16_t mfg_id = inw(base + E3C509_W0_MFG_ID);
|
||||
|
||||
/* 3Com manufacturer ID = 0x6D50 */
|
||||
if (mfg_id == 0x6D50) {
|
||||
return DRIVER_PROBE_OK;
|
||||
}
|
||||
|
||||
/* Also check for the product ID being in a reasonable range */
|
||||
if ((mfg_id & 0xFF00) == 0x9000 || (mfg_id & 0xFF00) == 0x9100) {
|
||||
return DRIVER_PROBE_OK;
|
||||
}
|
||||
|
||||
return DRIVER_PROBE_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the 3C509B driver.
|
||||
*/
|
||||
static int e3c509_driver_init(void) {
|
||||
memset(&e3c509_dev, 0, sizeof(e3c509_dev));
|
||||
|
||||
e3c509_dev.io_base = E3C509_DEFAULT_IOBASE;
|
||||
e3c509_dev.irq = E3C509_DEFAULT_IRQ;
|
||||
|
||||
/* Unmask IRQ */
|
||||
pic_clear_mask(e3c509_dev.irq);
|
||||
|
||||
/* Initialize hardware */
|
||||
if (e3c509_hw_init(e3c509_dev.io_base) != 0) {
|
||||
offset_print(" 3C509: initialization failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
e3c509_dev.present = 1;
|
||||
|
||||
/* Print MAC address */
|
||||
offset_print(" 3C509: MAC ");
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (i > 0) offset_print(":");
|
||||
print_hex(e3c509_dev.mac[i]);
|
||||
}
|
||||
offset_print("\n");
|
||||
|
||||
/* Register as character device (shares "eth" class with NE2000) */
|
||||
devicefs_register_char("eth", &e3c509_char_ops, &e3c509_dev);
|
||||
|
||||
offset_print(" 3C509: 10base-T (RJ45) on I/O ");
|
||||
print_hex(e3c509_dev.io_base);
|
||||
offset_print(" IRQ ");
|
||||
print_hex(e3c509_dev.irq);
|
||||
offset_print("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
e3c509_device_t *e3c509_get_device(void) {
|
||||
return e3c509_dev.present ? &e3c509_dev : NULL;
|
||||
}
|
||||
|
||||
int e3c509_init(void) {
|
||||
return e3c509_driver_init();
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Driver registration
|
||||
* ================================================================ */
|
||||
|
||||
static const driver_t e3c509_driver = {
|
||||
.name = "3c509b",
|
||||
.probe = e3c509_probe,
|
||||
.init = e3c509_driver_init,
|
||||
};
|
||||
|
||||
REGISTER_DRIVER(e3c509_driver);
|
||||
158
src/e3c509.h
Normal file
158
src/e3c509.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @file e3c509.h
|
||||
* @brief 3Com 3C509B (EtherLink III) ISA Ethernet NIC driver.
|
||||
*
|
||||
* Drives 3Com 3C509B NICs. Only supports RJ45 (10base-T) transceiver.
|
||||
* The 3C509B uses a windowed register model with 8 register windows
|
||||
* selected via the Window register. Packets are transferred through
|
||||
* PIO (programmed I/O) using the card's FIFO.
|
||||
*
|
||||
* Uses IRQ 10 by default for interrupt-driven operation.
|
||||
* Registers as a character device with devicefs (/dev/ethN).
|
||||
*/
|
||||
|
||||
#ifndef E3C509_H
|
||||
#define E3C509_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ================================================================
|
||||
* 3C509B I/O Port Layout
|
||||
*
|
||||
* The 3C509B uses 16 I/O ports starting at the base address.
|
||||
* Registers are organized into 8 windows (0-7).
|
||||
* Window selection: write window number to port base+0x0E.
|
||||
* ================================================================ */
|
||||
|
||||
/** Default ISA I/O base for 3C509B. */
|
||||
#define E3C509_DEFAULT_IOBASE 0x210
|
||||
|
||||
/** Default ISA IRQ for 3C509B. */
|
||||
#define E3C509_DEFAULT_IRQ 10
|
||||
|
||||
/* --- Global registers (always accessible) --- */
|
||||
#define E3C509_CMD 0x0E /**< Command register (write) */
|
||||
#define E3C509_STATUS 0x0E /**< Status register (read) */
|
||||
|
||||
/* --- Window 0: Setup / Configuration --- */
|
||||
#define E3C509_W0_MFG_ID 0x00 /**< Manufacturer ID (should be 0x6D50) */
|
||||
#define E3C509_W0_ADDR_CFG 0x06 /**< Address config (transceiver type) */
|
||||
#define E3C509_W0_RES_CFG 0x08 /**< Resource config (IRQ) */
|
||||
#define E3C509_W0_CFG_CTRL 0x04 /**< Config control */
|
||||
|
||||
/* --- Window 1: Operating Set --- */
|
||||
#define E3C509_W1_TX_PIO 0x00 /**< TX PIO data (write) */
|
||||
#define E3C509_W1_TX_STATUS 0x0B /**< TX status */
|
||||
#define E3C509_W1_RX_PIO 0x00 /**< RX PIO data (read) */
|
||||
#define E3C509_W1_RX_STATUS 0x08 /**< RX status */
|
||||
#define E3C509_W1_FREE_TX 0x0C /**< Free TX bytes */
|
||||
|
||||
/* --- Window 2: Station Address --- */
|
||||
#define E3C509_W2_ADDR0 0x00 /**< Station address word 0 */
|
||||
#define E3C509_W2_ADDR1 0x02 /**< Station address word 1 */
|
||||
#define E3C509_W2_ADDR2 0x04 /**< Station address word 2 */
|
||||
|
||||
/* --- Window 3: FIFO Management --- */
|
||||
/* (Used for internal FIFO buffer management) */
|
||||
|
||||
/* --- Window 4: Diagnostics --- */
|
||||
#define E3C509_W4_MEDIA_TYPE 0x0A /**< Media type and status */
|
||||
#define E3C509_W4_NET_DIAG 0x06 /**< Network diagnostics */
|
||||
|
||||
/* --- Window 5: Read Zeroes (for RX filter) --- */
|
||||
|
||||
/* --- Window 6: Statistics --- */
|
||||
#define E3C509_W6_TX_BYTES 0x0C /**< Total TX bytes */
|
||||
#define E3C509_W6_RX_BYTES 0x0A /**< Total RX bytes */
|
||||
|
||||
/* ================================================================
|
||||
* 3C509B Commands (written to command register)
|
||||
* ================================================================ */
|
||||
#define CMD_GLOBAL_RESET 0x0000 /**< Global reset */
|
||||
#define CMD_SELECT_WINDOW 0x0800 /**< Select window (OR with window num) */
|
||||
#define CMD_RX_ENABLE 0x2000 /**< Enable receiver */
|
||||
#define CMD_RX_RESET 0x2800 /**< Reset receiver */
|
||||
#define CMD_RX_DISCARD 0x4000 /**< Discard top RX packet */
|
||||
#define CMD_TX_ENABLE 0x4800 /**< Enable transmitter */
|
||||
#define CMD_TX_RESET 0x5800 /**< Reset transmitter */
|
||||
#define CMD_REQ_INTR 0x6000 /**< Request interrupt (OR with mask) */
|
||||
#define CMD_ACK_INTR 0x6800 /**< Acknowledge interrupt (OR with mask) */
|
||||
#define CMD_SET_INTR_MASK 0x7000 /**< Set interrupt mask */
|
||||
#define CMD_SET_RX_FILTER 0x8000 /**< Set RX filter */
|
||||
#define CMD_TX_DONE 0x8800 /**< ? */
|
||||
#define CMD_STATS_ENABLE 0x9000 /**< Enable statistics */
|
||||
#define CMD_STATS_DISABLE 0xB000 /**< Disable statistics */
|
||||
#define CMD_SET_TX_START 0x9800 /**< Set TX start threshold */
|
||||
|
||||
/* ================================================================
|
||||
* Status / Interrupt bits
|
||||
* ================================================================ */
|
||||
#define STAT_INT_LATCH 0x0001 /**< Interrupt latch */
|
||||
#define STAT_ADAPTER_FAIL 0x0002 /**< Adapter failure */
|
||||
#define STAT_TX_COMPLETE 0x0004 /**< TX complete */
|
||||
#define STAT_TX_AVAILABLE 0x0008 /**< TX available */
|
||||
#define STAT_RX_COMPLETE 0x0010 /**< RX complete */
|
||||
#define STAT_RX_EARLY 0x0020 /**< RX early threshold */
|
||||
#define STAT_INT_REQ 0x0040 /**< Interrupt requested */
|
||||
#define STAT_UPDATE_STATS 0x0080 /**< Update statistics */
|
||||
#define STAT_CMD_IN_PROG 0x1000 /**< Command in progress */
|
||||
#define STAT_WINDOW_MASK 0xE000 /**< Current window bits */
|
||||
|
||||
/* ================================================================
|
||||
* RX Filter bits (for CMD_SET_RX_FILTER)
|
||||
* ================================================================ */
|
||||
#define RX_FILTER_STATION 0x01 /**< Accept frames to station address */
|
||||
#define RX_FILTER_MCAST 0x02 /**< Accept multicast */
|
||||
#define RX_FILTER_BCAST 0x04 /**< Accept broadcast */
|
||||
#define RX_FILTER_PROMISC 0x08 /**< Promiscuous mode */
|
||||
|
||||
/* ================================================================
|
||||
* Transceiver types
|
||||
* ================================================================ */
|
||||
#define XCVR_TP 0x00 /**< 10base-T / RJ45 */
|
||||
#define XCVR_AUI 0x01 /**< AUI */
|
||||
#define XCVR_BNC 0x03 /**< BNC / 10base2 */
|
||||
|
||||
/** Maximum Ethernet frame size. */
|
||||
#define ETH_FRAME_MAX 1518
|
||||
|
||||
/* ================================================================
|
||||
* 3C509B device state
|
||||
* ================================================================ */
|
||||
typedef struct e3c509_device {
|
||||
uint16_t io_base; /**< I/O base address. */
|
||||
uint8_t irq; /**< IRQ number. */
|
||||
uint8_t mac[6]; /**< MAC address. */
|
||||
int present; /**< 1 if card detected. */
|
||||
} e3c509_device_t;
|
||||
|
||||
/* ================================================================
|
||||
* Public API
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Initialize 3C509B driver.
|
||||
*/
|
||||
int e3c509_init(void);
|
||||
|
||||
/**
|
||||
* 3C509B IRQ handler.
|
||||
*/
|
||||
void e3c509_irq(void);
|
||||
|
||||
/**
|
||||
* Send an Ethernet frame.
|
||||
*/
|
||||
int e3c509_send(e3c509_device_t *dev, const void *data, uint32_t len);
|
||||
|
||||
/**
|
||||
* Receive a pending Ethernet frame.
|
||||
*/
|
||||
int e3c509_recv(e3c509_device_t *dev, void *buf, uint32_t bufsize);
|
||||
|
||||
/**
|
||||
* Get the 3C509B device pointer.
|
||||
*/
|
||||
e3c509_device_t *e3c509_get_device(void);
|
||||
|
||||
#endif /* E3C509_H */
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "keyboard.h"
|
||||
#include "floppy.h"
|
||||
#include "ne2000.h"
|
||||
#include "e3c509.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* Forward declaration for kernel panic or similar */
|
||||
@@ -72,6 +73,9 @@ void isr_handler(registers_t *regs)
|
||||
} else if (regs->int_no == 41) {
|
||||
/* NE2000 Ethernet IRQ (IRQ 9, vector 41) */
|
||||
ne2k_irq();
|
||||
} else if (regs->int_no == 42) {
|
||||
/* 3C509B Ethernet IRQ (IRQ 10, vector 42) */
|
||||
e3c509_irq();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user