This commit is contained in:
Jia-Ju Bai 2016-10-22 16:09:41 +00:00 committed by GitHub
commit df9f74ce35
5 changed files with 1049 additions and 0 deletions

View File

@ -0,0 +1,14 @@
# Makefile for the VIA Technology 6105/6106S Ethernet driver (vt6105)
PROG= vt6105
SRCS= vt6105.c
FILES=${PROG}.conf
FILESNAME=${PROG}
FILESDIR= /etc/system.conf.d
DPADD+= ${LIBNETDRIVER} ${LIBSYS}
LDADD+= -lnetdriver -lsys
CPPFLAGS+= -I${NETBSDSRCDIR}/minix
.include <minix.service.mk>

View File

@ -0,0 +1,17 @@
The vt6105 driver is for VIA Technology 6105/6106S Ethernet card.
This driver is referred to Minix(3.4.0) rtl8169 driver,
Linux(4.2.1) via-rhine driver and VT6105 Rhine III Specification(Rev 1.2).
The supported PCI number is 1106:3106:1186:1403.
Maybe other PCI numbers for VIA Rhine III can be supported.
Revision 1.0 2016/10/18
Authored by Jia-Ju Bai <baijiaju1990@163.com>
Something can be improved:
1. MII, WOL functions are not adequately defined and used.
2. Link status report does not work well.
3. Ethernet address can not be modified at present.
4. Programming PHY registers and EEPROM are not adequately used.
5. Dump interfaces are not provided.

View File

@ -0,0 +1,807 @@
#include "vt6105.h"
/* global value */
static vt_driver g_driver;
static int g_instance;
/* I/O function */
static u8_t my_inb(u16_t port) {
u32_t value;
int r;
if ((r = sys_inb(port, &value)) != OK)
printf("VT6105: sys_inb failed: %d\n", r);
return (u8_t)value;
}
#define vt_inb(port, offset) (my_inb((port) + (offset)))
static u16_t my_inw(u16_t port) {
u32_t value;
int r;
if ((r = sys_inw(port, &value)) != OK)
printf("VT6105: sys_inw failed: %d\n", r);
return (u16_t)value;
}
#define vt_inw(port, offset) (my_inw((port) + (offset)))
static u32_t my_inl(u16_t port) {
u32_t value;
int r;
if ((r = sys_inl(port, &value)) != OK)
printf("VT6105: sys_inl failed: %d\n", r);
return value;
}
#define vt_inl(port, offset) (my_inl((port) + (offset)))
static void my_outb(u16_t port, u8_t value) {
int r;
if ((r = sys_outb(port, value)) != OK)
printf("VT6105: sys_outb failed: %d\n", r);
}
#define vt_outb(port, offset, value) (my_outb((port) + (offset), (value)))
static void my_outw(u16_t port, u16_t value) {
int r;
if ((r = sys_outw(port, value)) != OK)
printf("VT6105: sys_outw failed: %d\n", r);
}
#define vt_outw(port, offset, value) (my_outw((port) + (offset), (value)))
static void my_outl(u16_t port, u32_t value) {
int r;
if ((r = sys_outl(port, value)) != OK)
printf("VT6105: sys_outl failed: %d\n", r);
}
#define vt_outl(port, offset, value) (my_outl((port) + (offset), (value)))
/* driver interface */
static int vt_init(unsigned int instance, ether_addr_t *addr);
static void vt_stop(void);
static void vt_mode(unsigned int mode);
static ssize_t vt_recv(struct netdriver_data *data, size_t max);
static int vt_send(struct netdriver_data *data, size_t size);
static void vt_intr(unsigned int mask);
static void vt_stat(eth_stat_t *stat);
static void vt_alarm(clock_t stamp);
static int vt_probe(vt_driver *pdev, int instance);
static int vt_init_buf(vt_driver *pdev);
static int vt_init_hw(vt_driver *pdev, ether_addr_t *addr);
static void vt_reset_hw(vt_driver *pdev);
static void vt_power_init(vt_driver *pdev);
static void vt_conf_addr(vt_driver *pdev, ether_addr_t *addr);
static void vt_check_link(vt_driver *pdev);
static void vt_handler(vt_driver *pdev);
static void vt_check_ints(vt_driver *pdev);
static const struct netdriver vt_table = {
.ndr_init = vt_init,
.ndr_stop = vt_stop,
.ndr_mode = vt_mode,
.ndr_recv = vt_recv,
.ndr_send = vt_send,
.ndr_stat = vt_stat,
.ndr_intr = vt_intr,
.ndr_alarm = vt_alarm
};
int main(int argc, char *argv[]) {
env_setargs(argc, argv);
netdriver_task(&vt_table);
}
/* Initialize the driver */
static int vt_init(unsigned int instance, ether_addr_t *addr) {
int ret = 0;
/* Intialize driver data structure */
memset(&g_driver, 0, sizeof(g_driver));
g_driver.vt_link = VT_LINK_UNKNOWN;
strcpy(g_driver.vt_name, "vt6105#0");
g_driver.vt_name[7] += instance;
g_instance = instance;
/* Probe the device */
if (vt_probe(&g_driver, instance)) {
printf("VT6105: Device is not found!\n");
ret = -ENODEV;
goto err_probe;
}
/* Allocate and initialize buffer */
if (vt_init_buf(&g_driver)) {
printf("VT6105: Device is not found!\n");
ret = -ENODEV;
goto err_init_buf;
}
/* Intialize hardware */
if (vt_init_hw(&g_driver, addr)) {
printf("VT6105: Find to initialize hardware!\n");
ret = -EIO;
goto err_init_hw;
}
/* Use a synchronous alarm instead of a watchdog timer */
sys_setalarm(sys_hz(), 0);
/* Clear send and recv flag */
g_driver.vt_send_flag = FALSE;
g_driver.vt_recv_flag = FALSE;
return 0;
err_init_hw:
free_contig(g_driver.vt_buf, g_driver.vt_buf_size);
err_init_buf:
err_probe:
return ret;
}
/* Match the device and get base address */
static int vt_probe(vt_driver *pdev, int instance) {
int devind;
u16_t cr, vid, did;
u32_t bar;
u8_t irq, rev;
/* Find pci device */
pci_init();
if (!pci_first_dev(&devind, &vid, &did))
return -EIO;
while (instance--) {
if (!pci_next_dev(&devind, &vid, &did))
return -EIO;
}
pci_reserve(devind);
/* Enable bus mastering */
cr = pci_attr_r16(devind, PCI_CR);
if (!(cr & PCI_CR_MAST_EN))
pci_attr_w16(devind, PCI_CR, cr | PCI_CR_MAST_EN);
/* Get base address */
bar = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
if (bar < 0x400) {
printf("VT6105: base address is not properly configured!\n");
return -EIO;
}
pdev->vt_base_addr = bar;
/* Get irq number */
irq = pci_attr_r8(devind, PCI_ILR);
pdev->vt_irq = irq;
/* Get revision ID */
rev = pci_attr_r8(devind, PCI_REV);
pdev->vt_revision = rev;
#ifdef VT6105_DEBUG
printf("VT6105: Base address is 0x%08x\n", pdev->vt_base_addr);
printf("VT6105: IRQ number is 0x%08x\n", pdev->vt_irq);
printf("VT6105: Revision ID is 0x%08x\n", pdev->vt_revision);
#endif
return 0;
}
/* Allocate and initialize buffer */
static int vt_init_buf(vt_driver *pdev) {
size_t rx_desc_size, tx_desc_size, rx_buf_size, tx_buf_size, tot_buf_size;
vt_desc *desc;
phys_bytes buf_dma, next;
char *buf;
int i;
/* Build Rx and Tx descriptor buffer */
rx_desc_size = VT_RX_DESC_NUM * sizeof(vt_desc);
tx_desc_size = VT_TX_DESC_NUM * sizeof(vt_desc);
/* Allocate Rx and Tx buffer */
tx_buf_size = VT_TX_BUF_SIZE;
if (tx_buf_size % 4)
tx_buf_size += 4 - (tx_buf_size % 4);
rx_buf_size = VT_RX_BUF_SIZE;
tot_buf_size = rx_desc_size + tx_desc_size;
tot_buf_size += VT_TX_DESC_NUM * tx_buf_size + VT_RX_DESC_NUM * rx_buf_size;
if (tot_buf_size % 4096)
tot_buf_size += 4096 - (tot_buf_size % 4096);
if (!(buf = alloc_contig(tot_buf_size, 0, &buf_dma))) {
printf("VT6105: Fail to allocate memory!\n");
return -ENOMEM;
}
pdev->vt_buf_size = tot_buf_size;
pdev->vt_buf = buf;
/* Rx descriptor */
pdev->vt_rx_desc = (vt_desc *)buf;
pdev->vt_rx_desc_dma = buf_dma;
memset(buf, 0, rx_desc_size);
buf += rx_desc_size;
buf_dma += rx_desc_size;
/* Tx descriptor */
pdev->vt_tx_desc = (vt_desc *)buf;
pdev->vt_tx_desc_dma = buf_dma;
memset(buf, 0, tx_desc_size);
buf += tx_desc_size;
buf_dma += tx_desc_size;
/* Rx buffer assignment */
desc = pdev->vt_rx_desc;
next = pdev->vt_rx_desc_dma;
for (i = 0; i < VT_RX_DESC_NUM; i++) {
/* Set Rx buffer */
pdev->vt_rx[i].buf_dma = buf_dma;
pdev->vt_rx[i].buf = buf;
buf_dma += rx_buf_size;
buf += rx_buf_size;
/* Set Rx descriptor */
desc->status = VT_DESC_OWN |
((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK);
desc->addr = pdev->vt_rx[i].buf_dma;
desc->length = VT_RX_BUF_SIZE;
if (i == (VT_RX_DESC_NUM - 1))
desc->next_desc = pdev->vt_rx_desc_dma;
else {
next += sizeof(vt_desc);
desc->next_desc = next;
desc++;
}
}
/* Tx buffer assignment */
desc = pdev->vt_tx_desc;
next = pdev->vt_tx_desc_dma;
for (i = 0; i < VT_TX_DESC_NUM; i++) {
/* Set Tx buffer */
pdev->vt_tx[i].busy = 0;
pdev->vt_tx[i].buf_dma = buf_dma;
pdev->vt_tx[i].buf = buf;
buf_dma += tx_buf_size;
buf += tx_buf_size;
/* Set Rx descriptor */
desc->addr = pdev->vt_tx[i].buf_dma;
desc->length = VT_TX_BUF_SIZE;
if (i == (VT_TX_DESC_NUM - 1))
desc->next_desc = pdev->vt_tx_desc_dma;
else {
next += sizeof(vt_desc);
desc->next_desc = next;
desc++;
}
}
pdev->vt_tx_busy_num = 0;
pdev->vt_tx_head = 0;
pdev->vt_tx_tail = 0;
pdev->vt_rx_head = 0;
pdev->vt_tx_alive = FALSE;
return 0;
}
/* Intialize hardware */
static int vt_init_hw(vt_driver *pdev, ether_addr_t *addr) {
int r, ret;
/* Set the interrupt handler */
pdev->vt_hook = pdev->vt_irq;
if ((r = sys_irqsetpolicy(pdev->vt_irq, 0, &pdev->vt_hook)) != OK) {
printf("VT6105: Fail to set IRQ policy: %d!\n", r);
ret = -EFAULT;
goto err_irq_policy;
}
/* Reset hardware */
vt_reset_hw(pdev);
/* Enable IRQ */
if ((r = sys_irqenable(&pdev->vt_hook)) != OK) {
printf("VT6105: Fail to enable IRQ: %d!\n", r);
ret = -EFAULT;
goto err_irq_enable;
}
/* Configure MAC address */
vt_conf_addr(pdev, addr);
/* Detect link status */
vt_check_link(pdev);
#ifdef VT6105_DEBUG
if (pdev->vt_link)
printf("VT6105: Link up!\n");
else
printf("VT6105: Link down!\n");
#endif
return 0;
err_irq_enable:
err_irq_policy:
return ret;
}
/* Reset hardware */
static void vt_reset_hw(vt_driver *pdev) {
u16_t base = pdev->vt_base_addr;
u8_t db;
u16_t dw;
u32_t dl;
/* Let power registers into sane state */
vt_power_init(pdev);
/* Reset the chip */
vt_outw(base, VT_REG_CR, VT_CMD_RESET);
vt_inb(base, VT_REG_ADDR);
#ifdef VT6105_DEBUG
if (vt_inw(base, VT_REG_CR) & VT_CMD_RESET) {
vt_outb(base, VT_REG_MCR1, 0x40);
usleep(100000);
if (vt_inw(base, VT_REG_CR) & VT_CMD_RESET)
printf("VT6105: Fail to completely reset!\n");
}
else
printf("VT6105: Reset successfully!\n");
#endif
/* Initialize regsiters */
vt_outw(base, VT_REG_BCR0, 0x0006);
vt_outb(base, VT_REG_TCR, 0x20); /* Tx threshold is 256 bytes */
vt_outb(base, VT_REG_RCR, 0x78); /* Rx threshold is 256 bytes */
vt_outl(base, VT_REG_RD_BASE, pdev->vt_rx_desc_dma); /* Set Rx descriptor base address */
vt_outl(base, VT_REG_TD_BASE, pdev->vt_tx_desc_dma); /* Set Tx descriptor base address */
#ifdef VT6105_DEBUG
dl = vt_inl(base, VT_REG_RD_BASE);
printf("VT6105: Rx descriptor DMA address is: 0x%08x\n", dl);
dl = vt_inl(base, VT_REG_TD_BASE);
printf("VT6105: Tx descriptor DMA address is: 0x%08x\n", dl);
#endif
/* Enable interrupts */
vt_outw(base, VT_REG_IMR, 0xfeff);
/* Start the device, Rx and Tx */
dw = VT_CMD_START | VT_CMD_RX_ON | VT_CMD_TX_ON | VT_CMD_NO_POLL | VT_CMD_FDUPLEX;
vt_outw(base, VT_REG_CR, dw);
dw = vt_inw(base, VT_REG_CR);
#ifdef VT6105_DEBUG
dw = vt_inw(base, VT_REG_CR);
printf("VT6105: Control status is 0x%04x\n", dw);
#endif
}
/* Initialize power registers */
static void vt_power_init(vt_driver *pdev) {
u8_t db, wolstat;
u16_t dw, base = pdev->vt_base_addr;
u32_t dl;
/* Make sure chip is in power state D0 */
db = vt_inb(base, VT_REG_STICK);
vt_outb(base, VT_REG_STICK, db & 0xfc);
/* Disable "force PME-enable" */
vt_outb(base, VT_REG_WOLC_SET, 0x80);
/* Clear power-event config bits (WOL) */
vt_outb(base, VT_REG_WOL_SET, 0xff);
/* Save power-event status bits */
wolstat = vt_inb(base, VT_REG_WOLS_SET);
/* Clear power-event status bits */
vt_outb(base, VT_REG_WOLS_CLR, 0xff);
#ifdef VT6105_DEBUG
if (wolstat) {
char reason[50];
switch (wolstat) {
case VT_WOL_MAGIC:
strcpy(reason, "Magic packet");
break;
case VT_WOL_LINK_UP:
strcpy(reason, "Link up");
break;
case VT_WOL_LINK_DOWN:
strcpy(reason, "Link down");
break;
case VT_WOL_UCAST:
strcpy(reason, "Unicast packet");
break;
case VT_WOL_MCAST:
strcpy(reason, "Multicast/Broadcast packet");
break;
default:
strcpy(reason, "Unknown");
}
printf("VT6105: Wake system up: %s\n", reason);
}
#endif
}
static void vt_conf_addr(vt_driver *pdev, ether_addr_t *addr) {
u16_t dw, base = pdev->vt_base_addr;
int i;
for (i=0; i<6; i++)
addr->ea_addr[i] = vt_inb(base, VT_REG_ADDR + i);
#ifdef VT6105_DEBUG
printf("VT6105: Ethernet address is %02x:%02x:%02x:%02x:%02x:%02x\n",
addr->ea_addr[0], addr->ea_addr[1], addr->ea_addr[2],
addr->ea_addr[3], addr->ea_addr[4], addr->ea_addr[5]);
#endif
}
/* Detect link status (MII interface) */
static void vt_check_link(vt_driver *pdev) {
u16_t base = pdev->vt_base_addr;
u32_t res;
vt_outb(base, VT_REG_MII_CFG, 0x01);
vt_outb(base, VT_REG_MII_ADDR, 0x01);
vt_outb(base, VT_REG_MII_CR, 0x40);
usleep(10000);
res = vt_inw(base, VT_REG_MII_DATA);
if (res & 0x0004)
pdev->vt_link = TRUE;
else
pdev->vt_link = FALSE;
}
/* Stop the driver */
static void vt_stop(void) {
u16_t base = g_driver.vt_base_addr;
/* Free Rx and Tx buffer*/
free_contig(g_driver.vt_buf, g_driver.vt_buf_size);
/* Stop IRQ and device */
vt_outw(base, VT_REG_IMR, 0x0000);
vt_outw(base, VT_REG_CR, VT_CMD_STOP);
}
/* Set driver mode */
static void vt_mode(unsigned int mode) {
vt_driver *pdev = &g_driver;
u16_t base = pdev->vt_base_addr;
u8_t rcr;
pdev->vt_mode = mode;
vt_outl(base, VT_REG_MAR0, 0xffffffff);
vt_outl(base, VT_REG_MAR1, 0xffffffff);
rcr = vt_inb(base, VT_REG_RCR);
rcr &= ~(VT_RCR_AB | VT_RCR_AM | VT_RCR_AP | VT_RCR_AE | VT_RCR_AR);
if (pdev->vt_mode & NDEV_PROMISC)
rcr |= VT_RCR_AB | VT_RCR_AM | VT_RCR_AE | VT_RCR_AR;
if (pdev->vt_mode & NDEV_BROAD)
rcr |= VT_RCR_AB;
if (pdev->vt_mode & NDEV_MULTI)
rcr |= VT_RCR_AM;
rcr |= VT_RCR_AP;
vt_outb(base, VT_REG_RCR, 0x60 | rcr);
}
/* Receive data */
static ssize_t vt_recv(struct netdriver_data *data, size_t max) {
vt_driver *pdev = &g_driver;
u32_t rxstat, totlen, packlen;
vt_desc *desc;
int index, i;
index = pdev->vt_rx_head;
desc = pdev->vt_rx_desc;
desc += index;
/* Manage Rx buffer */
for (;;) {
rxstat = desc->status;
if (rxstat & VT_DESC_OWN)
return SUSPEND;
if (rxstat & VT_DESC_RX_ALL_ERR) {
#ifdef VT6105_DEBUG
printf("VT6105: Rx error: 0x%08x\n", rxstat);
#endif
if (rxstat & VT_DESC_RX_FOV) {
#ifdef VT6105_DEBUG
printf("VT6105: Rx buffer overflow\n");
#endif
pdev->vt_stat.ets_fifoOver++;
}
if (rxstat & VT_DESC_RX_FAE) {
#ifdef VT6105_DEBUG
printf("VT6105: Rx frames not align\n");
#endif
pdev->vt_stat.ets_frameAll++;
}
if (rxstat & VT_DESC_RX_CRCE) {
#ifdef VT6105_DEBUG
printf("VT6105: Rx CRC error\n");
#endif
pdev->vt_stat.ets_CRCerr++;
}
}
/* Normal packet */
if ((rxstat & VT_DESC_RX_PACK) == VT_DESC_RX_PACK)
break;
/* Other packet */
if (index == VT_RX_DESC_NUM - 1) {
desc->status = VT_DESC_OWN |
((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK);
index = 0;
desc = pdev->vt_rx_desc;
}
else {
desc->status = VT_DESC_OWN |
((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK);
index++;
desc++;
}
}
/* Get data length */
totlen = (rxstat & VT_DESC_RX_LENMASK) >> 16;
if (totlen < 8 || totlen > 2 * ETH_MAX_PACK_SIZE) {
printf("VT6105: Bad data length: %d\n", totlen);
panic(NULL);
}
/* Substract CRC to get packet */
packlen = totlen - ETH_CRC_SIZE;
if (packlen > max)
packlen = max;
/* Copy data to user */
netdriver_copyout(data, 0, pdev->vt_rx[index].buf, packlen);
pdev->vt_stat.ets_packetR++;
/* Set Rx descriptor status */
if (index == VT_RX_DESC_NUM - 1) {
desc->status = VT_DESC_OWN |
((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK);
index = 0;
}
else {
desc->status = VT_DESC_OWN |
((VT_RX_BUF_SIZE << 16) & VT_DESC_RX_LENMASK);
index++;
}
pdev->vt_rx_head = index;
#ifdef VT6105_DEBUG
printf("VT6105: Successfully receive a packet, length = %d\n", packlen);
#endif
return packlen;
}
/* Transmit data */
static int vt_send(struct netdriver_data *data, size_t size) {
vt_driver *pdev = &g_driver;
vt_desc *desc;
int tx_head, i;
u8_t db;
u16_t base = pdev->vt_base_addr;
tx_head = pdev->vt_tx_head;
desc = pdev->vt_tx_desc;
desc += tx_head;
if (pdev->vt_tx[tx_head].busy)
return SUSPEND;
/* Copy data from user */
netdriver_copyin(data, 0, pdev->vt_tx[tx_head].buf, size);
/* Set busy */
pdev->vt_tx[tx_head].busy = TRUE;
pdev->vt_tx_busy_num++;
/* Set Tx descriptor status */
desc->status = VT_DESC_OWN | VT_DESC_FIRST | VT_DESC_LAST;
desc->length = 0x00e08000 | (size >= 60 ? size : 60);
if (tx_head == VT_TX_DESC_NUM - 1)
tx_head = 0;
else
tx_head++;
pdev->vt_tx_head = tx_head;
/* Wake up transmit channel */
db = vt_inb(base, VT_REG_CR);
db |= VT_CMD_TX_DEMAND;
vt_outb(base, VT_REG_CR, db);
return 0;
}
/* Handle Interrupt */
static void vt_intr(unsigned int mask) {
int s;
/* Run interrupt handler at driver level */
vt_handler(&g_driver);
/* Reenable interrupts for this hook */
if ((s = sys_irqenable(&g_driver.vt_hook)) != OK)
printf("VT6105: Cannot enable interrupts: %d\n", s);
/* Perform tasks based on the flagged conditions */
vt_check_ints(&g_driver);
}
/* Real handler interrupt */
static void vt_handler(vt_driver *pdev) {
u16_t base = pdev->vt_base_addr;
int intr_status;
u8_t db;
u16_t dw;
u32_t dl, txstat;
int flag = 0, tx_head, tx_tail;
vt_desc *desc;
/* Get interrupt status */
intr_status = vt_inw(base, VT_REG_ISR);
/* Clear interrupt */
dw = intr_status & ~(VT_INTR_PCI_ERR | VT_INTR_PORT_CHANGE);
vt_outw(base, VT_REG_ISR, dw);
/* Check link status */
if (intr_status & VT_INTR_PORT_CHANGE)
printf("VT6105: Port state change!\n");
/* Check interrupt status */
if (intr_status & VT_INTR_RX_DONE) {
pdev->vt_recv_flag = TRUE;
flag++;
if (intr_status & VT_INTR_RX_ERR) {
pdev->vt_stat.ets_recvErr++;
printf("VT6105: Rx error in interrupt: 0x%04x\n", intr_status);
}
if (intr_status & VT_INTR_RX_NOBUF)
printf("VT6105: Rx no buffer in interrupt: 0x%04x\n", intr_status);
if (intr_status & VT_INTR_RX_EMPTY)
printf("VT6105: Rx buffer empty in interrupt: 0x%04x\n", intr_status);
if (intr_status & VT_INTR_RX_OVERFLOW)
printf("VT6105: Rx buffer overflow in interrupt: 0x%04x\n", intr_status);
if (intr_status & VT_INTR_RX_DROPPED)
printf("VT6105: Rx buffer dropped in interrupt: 0x%04x\n", intr_status);
}
if (intr_status & VT_INTR_TX_DONE) {
pdev->vt_send_flag = TRUE;
flag++;
if (intr_status & VT_INTR_TX_ERR) {
pdev->vt_stat.ets_sendErr++;
printf("VT6105: Tx error in interrupt: 0x%04x\n", intr_status);
}
if (intr_status & VT_INTR_TX_UNDER_RUN)
printf("VT6105: Tx buffer underflow in interrupt: 0x%04x\n", intr_status);
if (intr_status & VT_INTR_TX_ABORT)
printf("VT6105: Tx abort in interrupt: 0x%04x\n", intr_status);
/* Manage Tx Buffer */
tx_head = pdev->vt_tx_head;
tx_tail = pdev->vt_tx_tail;
while (tx_tail != tx_head) {
desc = pdev->vt_tx_desc;
desc += tx_tail;
if (!pdev->vt_tx[tx_tail].busy)
printf("VT6105: Strange, buffer not busy?\n");
txstat = desc->status;
/* Check whether the buffer is ready */
if (txstat & VT_DESC_OWN) {
break;
}
if (txstat & VT_DESC_TX_ERR) {
#ifdef VT6105_DEBUG
printf("VT6105: Tx error: 0x%08x\n", txstat);
#endif
if (txstat & VT_DESC_TX_UDF) {
#ifdef VT6105_DEBUG
printf("VT6105: Tx buffer underflow\n");
#endif
pdev->vt_stat.ets_fifoUnder++;
}
if (txstat & VT_DESC_TX_CRS) {
#ifdef VT6105_DEBUG
printf("VT6105: Tx carrier sense lost\n");
#endif
pdev->vt_stat.ets_carrSense++;
}
if (txstat & VT_DESC_TX_CDH) {
#ifdef VT6105_DEBUG
printf("VT6105: Tx collision detect\n");
#endif
pdev->vt_stat.ets_collision++;
}
if (txstat & VT_DESC_TX_OWC) {
#ifdef VT6105_DEBUG
printf("VT6105: Tx buffer out of winidow\n");
#endif
pdev->vt_stat.ets_OWC++;
}
}
pdev->vt_stat.ets_packetT++;
pdev->vt_tx[tx_tail].busy = FALSE;
pdev->vt_tx_busy_num--;
if (++tx_tail >= VT_TX_DESC_NUM)
tx_tail = 0;
pdev->vt_send_flag = TRUE;
pdev->vt_recv_flag = TRUE;
pdev->vt_tx_alive = TRUE;
#ifdef VT6105_DEBUG
printf("VT6105: Successfully send a packet\n");
#endif
}
pdev->vt_tx_tail = tx_tail;
}
if (!flag) {
printf("VT6105: Unknown error in interrupt: 0x%04x\n", intr_status);
return;
}
/* Perform tasks based on the flagged condition */
vt_check_ints(pdev);
}
/* Check interrupt and perform */
static void vt_check_ints(vt_driver *pdev) {
if (!pdev->vt_recv_flag)
return;
pdev->vt_recv_flag = FALSE;
/* Handle data receive */
netdriver_recv();
/* Handle data transmit */
if (pdev->vt_send_flag) {
pdev->vt_send_flag = FALSE;
netdriver_send();
}
}
static void vt_stat(eth_stat_t *stat) {
memcpy(stat, &g_driver.vt_stat, sizeof(*stat));
}
static void vt_alarm(clock_t stamp) {
vt_driver *pdev = &g_driver;
/* Use a synchronous alarm instead of a watchdog timer */
sys_setalarm(sys_hz(), 0);
/* Assume the an idle system is alive */
if (!pdev->vt_tx_busy_num) {
pdev->vt_tx_alive = TRUE;
return;
}
if (pdev->vt_tx_alive) {
pdev->vt_tx_alive = FALSE;
return;
}
printf("VT6105: Resetting the driver\n");
vt_reset_hw(pdev);
pdev->vt_recv_flag = TRUE;
vt_check_ints(pdev);
}

View File

@ -0,0 +1,17 @@
service vt6105
{
type net;
descr "VIA Technology 6105/6106S Ethernet Card";
system
UMAP # 14
IRQCTL # 19
DEVIO # 21
;
pci device 1106:3053;
pci device 1106:3106;
ipc
SYSTEM pm rs log tty ds vm
pci inet lwip amddev
;
};

View File

@ -0,0 +1,194 @@
#include <minix/drivers.h>
#include <minix/netdriver.h>
#include <machine/pci.h>
#include <minix/ds.h>
#include <assert.h>
/* PCI Register */
#define VT_PCI_VID 0x00 /* Vendor ID */
#define VT_PCI_DID 0x02 /* Device ID */
#define VT_PCI_CMD 0x04 /* Command */
#define VT_PCI_STS 0x06 /* Status */
#define VT_PCI_RID 0x08 /* Revision ID */
#define VT_PCI_CC 0x09 /* Class Code */
#define VT_PCI_CLS 0x0c /* Cache Line Size */
#define VT_PCI_LT 0x0d /* Latency Timer */
#define VT_PCI_HT 0x0e /* Header Type */
/* Internal Register */
#define VT_REG_ADDR 0x00 /* Ethernet Address */
#define VT_REG_RCR 0x06 /* Receive Configure Request */
#define VT_REG_TCR 0x07 /* Transmit Configure Request */
#define VT_REG_CR 0x08 /* Control 0 */
#define VT_REG_ISR 0x0c /* Interrupt Status */
#define VT_REG_IMR 0x0e /* Interrupt Mask */
#define VT_REG_MAR0 0x10 /* Multicast Address 0 */
#define VT_REG_MAR1 0x14 /* Multicast Address 1 */
#define VT_REG_RD_BASE 0x18 /* Receive Descriptor Base Address */
#define VT_REG_TD_BASE 0x1c /* Transmit Descriptor Base Address */
#define VT_REG_MII_CFG 0x6c /* MII Configuration */
#define VT_REG_MII_STS 0x6d /* MII Status */
#define VT_REG_BCR0 0x6e /* Bus Control 0 */
#define VT_REG_BCR1 0x6f /* Bus Control 1 */
#define VT_REG_MII_CR 0x70 /* MII Control */
#define VT_REG_MII_ADDR 0x71 /* MII Interface Address*/
#define VT_REG_MII_DATA 0x72 /* MII Data Port */
#define VT_REG_EECSR 0x74 /* EEPROM Control/Status */
#define VT_REG_CFG_A 0x78 /* Chip Configuration A */
#define VT_REG_CFG_B 0x79 /* Chip Configuration B */
#define VT_REG_CFG_C 0x7a /* Chip Configuration C */
#define VT_REG_CFG_D 0x7b /* Chip Configuration D */
#define VT_REG_MCR0 0x80 /* Miscellaneous Control 0 */
#define VT_REG_MCR1 0x81 /* Miscellaneous Control 1 */
#define VT_REG_STICK 0x83 /* Sticky Hardware Control */
#define VT_REG_MISR 0x84 /* MII Interrupt Status */
#define VT_REG_MIMR 0x86 /* MII Interrupt Mask */
#define VT_REG_FCR0 0x98 /* Flow Control 0 */
#define VT_REG_FCR1 0x99 /* Flow Control 1 */
#define VT_REG_WOL_SET 0xA4 /* Wake On LAN Set */
#define VT_REG_WOL_CLR 0xA0 /* Wake On LAN Clear */
#define VT_REG_PCS_CLR 0xA5 /* Power Configuration Set */
#define VT_REG_PCS_SET 0xA1 /* Power Configuration Clear */
#define VT_REG_WOLC_SET 0xA7 /* WOL Configuration Set */
#define VT_REG_WOLC_CLR 0xA3 /* WOL Configuration Clear */
#define VT_REG_WOLS_SET 0xA8 /* WOL Status Clear */
#define VT_REG_WOLS_CLR 0xAC /* WOL Status Set */
/* Interrupt Status/Mask Register */
#define VT_INTR_RX_DONE 0x0001
#define VT_INTR_TX_DONE 0x0002
#define VT_INTR_RX_ERR 0x0004
#define VT_INTR_TX_ERR 0x0008
#define VT_INTR_RX_EMPTY 0x0020
#define VT_INTR_PCI_ERR 0x0040
#define VT_INTR_STS_MAX 0x0080
#define VT_INTR_RX_EARLY 0x0100
#define VT_INTR_TX_UNDER_RUN 0x0210
#define VT_INTR_RX_OVERFLOW 0x0400
#define VT_INTR_RX_DROPPED 0x0800
#define VT_INTR_RX_NOBUF 0x1000
#define VT_INTR_TX_ABORT 0x2000
#define VT_INTR_PORT_CHANGE 0x4000
#define VT_INTR_RX_WAKE_UP 0x8000
/* Control Register Command */
#define VT_CMD_START 0x0002
#define VT_CMD_STOP 0x0004
#define VT_CMD_RX_ON 0x0008
#define VT_CMD_TX_ON 0x0010
#define VT_CMD_TX_DEMAND 0x0020
#define VT_CMD_RX_DEMAND 0x0040
#define VT_CMD_EARLY_RX 0x0100
#define VT_CMD_EARLY_TX 0x0200
#define VT_CMD_FDUPLEX 0x0400
#define VT_CMD_NO_POLL 0x0800
#define VT_CMD_RESET 0x8000
/* Receive Configure Mode */
#define VT_RCR_AP 0x10
#define VT_RCR_AB 0x08
#define VT_RCR_AM 0x04
#define VT_RCR_AR 0x02
#define VT_RCR_AE 0x01
/* General Descriptor Flag */
#define VT_DESC_OWN 0x80000000 /* Ownership */
#define VT_DESC_FIRST 0x00000200 /* First Descriptor */
#define VT_DESC_LAST 0x00000100 /* Last Descriptor */
/* Rx Descriptor Flag */
#define VT_DESC_RX_LENMASK 0x7fff0000 /* Receive Frame Length Mask */
#define VT_DESC_RX_OK 0x00008000 /* Receive OK */
#define VT_DESC_RX_MAR 0x00002000 /* Multicast Address Received */
#define VT_DESC_RX_BAR 0x00001000 /* Broadcast Address Received */
#define VT_DESC_RX_PHY 0x00000800 /* Physical Address Received */
#define VT_DESC_RX_LINK_ERR 0x00000080 /* Descriptor Link Structure Error */
#define VT_DESC_RX_RUNT 0x00000020 /* Runt Packet */
#define VT_DESC_RX_LONG 0x00000010 /* Long Packet */
#define VT_DESC_RX_FOV 0x00000008 /* FIFO Overflow */
#define VT_DESC_RX_FAE 0x00000004 /* Frame Align Error */
#define VT_DESC_RX_CRCE 0x00000002 /* CRC Error */
#define VT_DESC_RX_ERR 0x00000001 /* Receive Error */
#define VT_DESC_RX_ALL_ERR (VT_DESC_RX_LINK_ERR | VT_DESC_RX_RUNT | \
VT_DESC_RX_LONG | VT_DESC_RX_FOV | \
VT_DESC_RX_FAE | VT_DESC_RX_CRCE | VT_DESC_RX_ERR)
#define VT_DESC_RX_PACK (VT_DESC_FIRST | VT_DESC_LAST)
/* Tx Descriptor Flag */
#define VT_DESC_TX_ERR 0x00008000 /* Transmit Error */
#define VT_DESC_TX_UDF 0x00000800 /* FIFO Underflow */
#define VT_DESC_TX_CRS 0x00000400 /* Carrier Sense Lost Detect */
#define VT_DESC_TX_OWC 0x00000200 /* Out of Window Collision */
#define VT_DESC_TX_ABT 0x00000100 /* Excessive Collision Abort */
#define VT_DESC_TX_CDH 0x00000010 /* Collision Detect */
/* WOL status */
#define VT_WOL_UCAST 0x10
#define VT_WOL_MAGIC 0x20
#define VT_WOL_MCAST 0x30
#define VT_WOL_LINK_UP 0x40
#define VT_WOL_LINK_DOWN 0x80
/* Link status */
#define VT_LINK_UP 1
#define VT_LINK_DOWN 0
#define VT_LINK_UNKNOWN -1
#define VT_RX_BUF_SIZE 1536
#define VT_TX_BUF_SIZE 1536
#define VT_RX_DESC_NUM 64
#define VT_TX_DESC_NUM 64
/* #define VT6105_DEBUG */
/* Data Descriptor */
typedef struct vt_desc {
u32_t status; /* command/status */
u32_t length; /* Buffer/Frame length */
u32_t addr; /* DMA buffer address */
u32_t next_desc; /* Next descriptor */
} vt_desc;
/* Driver Data Structure */
typedef struct vt_driver {
u16_t vt_base_addr; /* Base address */
int vt_revision; /* Revision ID */
int vt_irq; /* IRQ number */
int vt_mode;
int vt_link; /* Whether link-up */
int vt_recv_flag; /* Receive flag */
int vt_send_flag; /* Send flag */
int vt_report_link;
int vt_tx_alive;
int vt_tx_busy;
/* Buffer */
size_t vt_buf_size;
char *vt_buf;
/* Rx data */
int vt_rx_head;
struct {
phys_bytes buf_dma;
char *buf;
} vt_rx[VT_RX_DESC_NUM];
vt_desc *vt_rx_desc; /* Rx descriptor buffer */
phys_bytes vt_rx_desc_dma; /* Rx descriptor DMA buffer */
/* Tx data */
int vt_tx_head;
int vt_tx_tail;
struct {
int busy;
phys_bytes buf_dma;
char *buf;
} vt_tx[VT_TX_DESC_NUM];
vt_desc *vt_tx_desc; /* Tx descriptor buffer */
phys_bytes vt_tx_desc_dma; /* Tx descriptor DMA buffer */
int vt_tx_busy_num; /* Number of busy Tx descriptors */
int vt_hook; /* IRQ hook id at kernel */
eth_stat_t vt_stat;
char vt_name[20];
} vt_driver;