atl2: use new libnetdriver
Change-Id: I65de5d29a75d5f3101b2c5ab16a4b0d79bd77cc3
This commit is contained in:
parent
dbcce9ddb0
commit
b80fc5be89
@ -7,18 +7,14 @@
|
|||||||
#include <minix/drivers.h>
|
#include <minix/drivers.h>
|
||||||
#include <minix/netdriver.h>
|
#include <minix/netdriver.h>
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <minix/ds.h>
|
|
||||||
#include <minix/vm.h>
|
|
||||||
#include <machine/pci.h>
|
#include <machine/pci.h>
|
||||||
#include <net/gen/ether.h>
|
#include <sys/mman.h>
|
||||||
#include <net/gen/eth_io.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include "atl2.h"
|
#include "atl2.h"
|
||||||
|
|
||||||
#define VERBOSE 0 /* Verbose debugging output */
|
#define VERBOSE 0 /* Verbose debugging output */
|
||||||
#define ATL2_FKEY 1 /* Register Shift+F11 for dumping statistics */
|
#define ATL2_FKEY 11 /* Use Shift+Fn to dump statistics (0=off) */
|
||||||
|
|
||||||
#if VERBOSE
|
#if VERBOSE
|
||||||
#define ATL2_DEBUG(x) printf x
|
#define ATL2_DEBUG(x) printf x
|
||||||
@ -36,8 +32,7 @@ static struct {
|
|||||||
int devind; /* PCI device index */
|
int devind; /* PCI device index */
|
||||||
int irq; /* IRQ number */
|
int irq; /* IRQ number */
|
||||||
int hook_id; /* IRQ hook ID */
|
int hook_id; /* IRQ hook ID */
|
||||||
int mode; /* datalink mode */
|
uint8_t *base; /* base address of memory-mapped registers */
|
||||||
volatile uint8_t *base; /* base address of memory-mapped registers */
|
|
||||||
uint32_t size; /* size of memory-mapped area */
|
uint32_t size; /* size of memory-mapped area */
|
||||||
uint32_t hwaddr[2]; /* MAC address, in register representation */
|
uint32_t hwaddr[2]; /* MAC address, in register representation */
|
||||||
|
|
||||||
@ -58,21 +53,11 @@ static struct {
|
|||||||
int txs_num; /* head-tail offset into TxS, in elements */
|
int txs_num; /* head-tail offset into TxS, in elements */
|
||||||
int rxd_tail; /* tail index into RxD, in elements */
|
int rxd_tail; /* tail index into RxD, in elements */
|
||||||
|
|
||||||
int flags; /* state flags (ATL2_FLAG_) */
|
int rx_avail; /* is there a packet available for receipt? */
|
||||||
message read_msg; /* suspended read request (READ_PEND) */
|
|
||||||
message write_msg; /* suspended write request (WRITE_PEND) */
|
|
||||||
endpoint_t task_endpt; /* requester endpoint (PACK_RCVD|PACK_SENT) */
|
|
||||||
size_t recv_count; /* packet size (PACK_RCVD) */
|
|
||||||
|
|
||||||
eth_stat_t stat; /* statistics */
|
eth_stat_t stat; /* statistics */
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
#define ATL2_FLAG_RX_AVAIL 0x01 /* packet available for receipt */
|
|
||||||
#define ATL2_FLAG_READ_PEND 0x02 /* read request pending */
|
|
||||||
#define ATL2_FLAG_WRITE_PEND 0x04 /* write request pending */
|
|
||||||
#define ATL2_FLAG_PACK_RCVD 0x08 /* packet received */
|
|
||||||
#define ATL2_FLAG_PACK_SENT 0x10 /* packet transmitted */
|
|
||||||
|
|
||||||
#define ATL2_READ_U8(off) (*(volatile uint8_t *)(state.base + (off)))
|
#define ATL2_READ_U8(off) (*(volatile uint8_t *)(state.base + (off)))
|
||||||
#define ATL2_READ_U16(off) (*(volatile uint16_t *)(state.base + (off)))
|
#define ATL2_READ_U16(off) (*(volatile uint16_t *)(state.base + (off)))
|
||||||
#define ATL2_READ_U32(off) (*(volatile uint32_t *)(state.base + (off)))
|
#define ATL2_READ_U32(off) (*(volatile uint32_t *)(state.base + (off)))
|
||||||
@ -85,7 +70,25 @@ static struct {
|
|||||||
|
|
||||||
#define ATL2_ALIGN_32(n) (((n) + 3) & ~3)
|
#define ATL2_ALIGN_32(n) (((n) + 3) & ~3)
|
||||||
|
|
||||||
static iovec_s_t iovec[NR_IOREQS];
|
static int atl2_init(unsigned int instance, ether_addr_t *addr);
|
||||||
|
static void atl2_stop(void);
|
||||||
|
static void atl2_mode(unsigned int mode);
|
||||||
|
static int atl2_send(struct netdriver_data *data, size_t size);
|
||||||
|
static ssize_t atl2_recv(struct netdriver_data *data, size_t max);
|
||||||
|
static void atl2_stat(eth_stat_t *stat);
|
||||||
|
static void atl2_intr(unsigned int mask);
|
||||||
|
static void atl2_other(const message *m_ptr, int ipc_status);
|
||||||
|
|
||||||
|
static const struct netdriver atl2_table = {
|
||||||
|
.ndr_init = atl2_init,
|
||||||
|
.ndr_stop = atl2_stop,
|
||||||
|
.ndr_mode = atl2_mode,
|
||||||
|
.ndr_recv = atl2_recv,
|
||||||
|
.ndr_send = atl2_send,
|
||||||
|
.ndr_stat = atl2_stat,
|
||||||
|
.ndr_intr = atl2_intr,
|
||||||
|
.ndr_other = atl2_other
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read a value from the VPD register area.
|
* Read a value from the VPD register area.
|
||||||
@ -178,7 +181,7 @@ atl2_get_vpd_hwaddr(void)
|
|||||||
* use whatever the card was already set to.
|
* use whatever the card was already set to.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
atl2_get_hwaddr(void)
|
atl2_get_hwaddr(ether_addr_t * addr)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!atl2_get_vpd_hwaddr()) {
|
if (!atl2_get_vpd_hwaddr()) {
|
||||||
@ -188,8 +191,15 @@ atl2_get_hwaddr(void)
|
|||||||
state.hwaddr[1] = ATL2_READ_U32(ATL2_HWADDR1_REG) & 0xffff;
|
state.hwaddr[1] = ATL2_READ_U32(ATL2_HWADDR1_REG) & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
ATL2_DEBUG(("ATL2: MAC address %04lx%08lx\n",
|
ATL2_DEBUG(("ATL2: MAC address %04x%08x\n",
|
||||||
state.hwaddr[1], state.hwaddr[0]));
|
state.hwaddr[1], state.hwaddr[0]));
|
||||||
|
|
||||||
|
addr->ea_addr[0] = state.hwaddr[1] >> 8;
|
||||||
|
addr->ea_addr[1] = state.hwaddr[1] & 0xff;
|
||||||
|
addr->ea_addr[2] = state.hwaddr[0] >> 24;
|
||||||
|
addr->ea_addr[3] = (state.hwaddr[0] >> 16) & 0xff;
|
||||||
|
addr->ea_addr[4] = (state.hwaddr[0] >> 8) & 0xff;
|
||||||
|
addr->ea_addr[5] = state.hwaddr[0] & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -260,7 +270,7 @@ atl2_alloc_dma(void)
|
|||||||
/*
|
/*
|
||||||
* Stop the device.
|
* Stop the device.
|
||||||
*/
|
*/
|
||||||
static int
|
static void
|
||||||
atl2_stop(void)
|
atl2_stop(void)
|
||||||
{
|
{
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
@ -288,8 +298,7 @@ atl2_stop(void)
|
|||||||
micro_delay(ATL2_IDLE_DELAY);
|
micro_delay(ATL2_IDLE_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The caller will generally ignore this return value. */
|
assert(i < ATL2_IDLE_NTRIES);
|
||||||
return (i < ATL2_IDLE_NTRIES);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -331,18 +340,18 @@ atl2_reset(void)
|
|||||||
* settings.
|
* settings.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
atl2_set_mode(void)
|
atl2_mode(unsigned int mode)
|
||||||
{
|
{
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
|
|
||||||
val = ATL2_READ_U32(ATL2_MAC_REG);
|
val = ATL2_READ_U32(ATL2_MAC_REG);
|
||||||
val &= ~(ATL2_MAC_PROMISC_EN | ATL2_MAC_MCAST_EN | ATL2_MAC_BCAST_EN);
|
val &= ~(ATL2_MAC_PROMISC_EN | ATL2_MAC_MCAST_EN | ATL2_MAC_BCAST_EN);
|
||||||
|
|
||||||
if (state.mode & DL_PROMISC_REQ)
|
if (mode & NDEV_PROMISC)
|
||||||
val |= ATL2_MAC_PROMISC_EN;
|
val |= ATL2_MAC_PROMISC_EN;
|
||||||
if (state.mode & DL_MULTI_REQ)
|
if (mode & NDEV_MULTI)
|
||||||
val |= ATL2_MAC_MCAST_EN;
|
val |= ATL2_MAC_MCAST_EN;
|
||||||
if (state.mode & DL_BROAD_REQ)
|
if (mode & NDEV_BROAD)
|
||||||
val |= ATL2_MAC_BCAST_EN;
|
val |= ATL2_MAC_BCAST_EN;
|
||||||
|
|
||||||
ATL2_WRITE_U32(ATL2_MAC_REG, val);
|
ATL2_WRITE_U32(ATL2_MAC_REG, val);
|
||||||
@ -409,7 +418,7 @@ atl2_setup(void)
|
|||||||
/* Reset descriptors, and enable DMA. */
|
/* Reset descriptors, and enable DMA. */
|
||||||
state.txd_tail = state.txs_tail = state.rxd_tail = 0;
|
state.txd_tail = state.txs_tail = state.rxd_tail = 0;
|
||||||
state.txd_num = state.txs_num = 0;
|
state.txd_num = state.txs_num = 0;
|
||||||
state.flags &= ~ATL2_FLAG_RX_AVAIL;
|
state.rx_avail = FALSE;
|
||||||
ATL2_WRITE_U16(ATL2_TXD_IDX_REG, 0);
|
ATL2_WRITE_U16(ATL2_TXD_IDX_REG, 0);
|
||||||
ATL2_WRITE_U16(ATL2_RXD_IDX_REG, 0);
|
ATL2_WRITE_U16(ATL2_RXD_IDX_REG, 0);
|
||||||
|
|
||||||
@ -440,7 +449,7 @@ atl2_setup(void)
|
|||||||
ATL2_WRITE_U32(ATL2_MHT0_REG, 0xffffffff);
|
ATL2_WRITE_U32(ATL2_MHT0_REG, 0xffffffff);
|
||||||
ATL2_WRITE_U32(ATL2_MHT1_REG, 0xffffffff);
|
ATL2_WRITE_U32(ATL2_MHT1_REG, 0xffffffff);
|
||||||
|
|
||||||
atl2_set_mode();
|
atl2_mode(NDEV_NOMODE);
|
||||||
|
|
||||||
/* Enable Tx/Rx. */
|
/* Enable Tx/Rx. */
|
||||||
val = ATL2_READ_U32(ATL2_MAC_REG);
|
val = ATL2_READ_U32(ATL2_MAC_REG);
|
||||||
@ -488,18 +497,13 @@ atl2_probe(int skip)
|
|||||||
* Initialize the device.
|
* Initialize the device.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
atl2_init(int devind)
|
atl2_init_hw(int devind, ether_addr_t * addr)
|
||||||
{
|
{
|
||||||
uint32_t bar;
|
uint32_t bar;
|
||||||
int r, flag;
|
int r, flag;
|
||||||
|
|
||||||
/* Initialize global state. */
|
/* Initialize global state. */
|
||||||
state.devind = devind;
|
state.devind = devind;
|
||||||
state.mode = DL_NOMODE;
|
|
||||||
state.flags = 0;
|
|
||||||
state.recv_count = 0;
|
|
||||||
|
|
||||||
memset(&state.stat, 0, sizeof(state.stat));
|
|
||||||
|
|
||||||
if ((r = pci_get_bar(devind, PCI_BAR, &bar, &state.size, &flag)) != OK)
|
if ((r = pci_get_bar(devind, PCI_BAR, &bar, &state.size, &flag)) != OK)
|
||||||
panic("unable to retrieve bar: %d", r);
|
panic("unable to retrieve bar: %d", r);
|
||||||
@ -526,7 +530,7 @@ atl2_init(int devind)
|
|||||||
if ((r = sys_irqenable(&state.hook_id)) != OK)
|
if ((r = sys_irqenable(&state.hook_id)) != OK)
|
||||||
panic("unable to enable IRQ: %d", r);
|
panic("unable to enable IRQ: %d", r);
|
||||||
|
|
||||||
atl2_get_hwaddr();
|
atl2_get_hwaddr(addr);
|
||||||
|
|
||||||
atl2_setup();
|
atl2_setup();
|
||||||
}
|
}
|
||||||
@ -605,7 +609,8 @@ atl2_tx_advance(void)
|
|||||||
|
|
||||||
assert((uint32_t)state.txd_tail <=
|
assert((uint32_t)state.txd_tail <=
|
||||||
ATL2_TXD_BUFSIZE - sizeof(uint32_t));
|
ATL2_TXD_BUFSIZE - sizeof(uint32_t));
|
||||||
dsize = *(uint32_t *)(state.txd_base + state.txd_tail);
|
dsize =
|
||||||
|
*(volatile uint32_t *)(state.txd_base + state.txd_tail);
|
||||||
if (size != dsize)
|
if (size != dsize)
|
||||||
printf("ATL2: TxD/TxS size mismatch (%x vs %x)\n",
|
printf("ATL2: TxD/TxS size mismatch (%x vs %x)\n",
|
||||||
size, dsize);
|
size, dsize);
|
||||||
@ -643,7 +648,8 @@ atl2_rx_advance(int next)
|
|||||||
{
|
{
|
||||||
int update_tail;
|
int update_tail;
|
||||||
rxd_t *rxd;
|
rxd_t *rxd;
|
||||||
uint32_t hdr, size;
|
uint32_t hdr;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
update_tail = FALSE;
|
update_tail = FALSE;
|
||||||
|
|
||||||
@ -653,10 +659,10 @@ atl2_rx_advance(int next)
|
|||||||
|
|
||||||
ATL2_DEBUG(("ATL2: successfully received packet\n"));
|
ATL2_DEBUG(("ATL2: successfully received packet\n"));
|
||||||
|
|
||||||
state.flags &= ~ATL2_FLAG_RX_AVAIL;
|
state.rx_avail = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!(state.flags & ATL2_FLAG_RX_AVAIL));
|
assert(!state.rx_avail);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Check the RxD tail for updates. */
|
/* Check the RxD tail for updates. */
|
||||||
@ -678,11 +684,12 @@ atl2_rx_advance(int next)
|
|||||||
*/
|
*/
|
||||||
size = hdr & ATL2_RXD_SIZE_MASK;
|
size = hdr & ATL2_RXD_SIZE_MASK;
|
||||||
|
|
||||||
if ((hdr & ATL2_RXD_SUCCESS) && size >= ETH_MIN_PACK_SIZE) {
|
if ((hdr & ATL2_RXD_SUCCESS) &&
|
||||||
ATL2_DEBUG(("ATL2: packet available, size %ld\n",
|
size >= ETH_MIN_PACK_SIZE + ETH_CRC_SIZE) {
|
||||||
|
ATL2_DEBUG(("ATL2: packet available, size %zu\n",
|
||||||
size));
|
size));
|
||||||
|
|
||||||
state.flags |= ATL2_FLAG_RX_AVAIL;
|
state.rx_avail = TRUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,220 +709,78 @@ atl2_rx_advance(int next)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send a task reply to Inet.
|
* Receive a packet.
|
||||||
*/
|
*/
|
||||||
static void
|
static ssize_t
|
||||||
atl2_reply(void)
|
atl2_recv(struct netdriver_data * data, size_t max)
|
||||||
{
|
|
||||||
message m;
|
|
||||||
int r, flags;
|
|
||||||
|
|
||||||
flags = DL_NOFLAGS;
|
|
||||||
if (state.flags & ATL2_FLAG_PACK_SENT)
|
|
||||||
flags |= DL_PACK_SEND;
|
|
||||||
if (state.flags & ATL2_FLAG_PACK_RCVD)
|
|
||||||
flags |= DL_PACK_RECV;
|
|
||||||
|
|
||||||
m.m_type = DL_TASK_REPLY;
|
|
||||||
m.m_netdrv_net_dl_task.flags = flags;
|
|
||||||
m.m_netdrv_net_dl_task.count = state.recv_count;
|
|
||||||
|
|
||||||
ATL2_DEBUG(("ATL2: sending reply, flags %x count %d\n", flags,
|
|
||||||
m.m_netdrv_net_dl_task.count));
|
|
||||||
|
|
||||||
if ((r = ipc_send(state.task_endpt, &m)) != OK)
|
|
||||||
panic("unable to reply: %d", r);
|
|
||||||
|
|
||||||
state.flags &= ~(ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD);
|
|
||||||
state.recv_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read packet data.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
atl2_readv(const message * m, int from_int)
|
|
||||||
{
|
{
|
||||||
rxd_t *rxd;
|
rxd_t *rxd;
|
||||||
iovec_s_t *iovp;
|
size_t size;
|
||||||
size_t count, off, left, size;
|
|
||||||
uint8_t *pos;
|
|
||||||
int i, j, r, batch;
|
|
||||||
|
|
||||||
/* We can deal with only one read request from Inet at a time. */
|
|
||||||
assert(from_int || !(state.flags & ATL2_FLAG_READ_PEND));
|
|
||||||
|
|
||||||
state.task_endpt = m->m_source;
|
|
||||||
|
|
||||||
/* Are there any packets available at all? */
|
/* Are there any packets available at all? */
|
||||||
if (!(state.flags & ATL2_FLAG_RX_AVAIL))
|
if (!state.rx_avail)
|
||||||
goto suspend;
|
return SUSPEND;
|
||||||
|
|
||||||
/* Get the first available packet's size. Cut off the CRC. */
|
/* Get the first available packet's size. Cut off the CRC. */
|
||||||
rxd = &state.rxd_base[state.rxd_tail];
|
rxd = &state.rxd_base[state.rxd_tail];
|
||||||
|
|
||||||
count = rxd->hdr & ATL2_RXD_SIZE_MASK;
|
size = rxd->hdr & ATL2_RXD_SIZE_MASK;
|
||||||
count -= ETH_CRC_SIZE;
|
size -= ETH_CRC_SIZE;
|
||||||
|
|
||||||
ATL2_DEBUG(("ATL2: readv: found packet with length %d\n", count));
|
ATL2_DEBUG(("ATL2: receiving packet with length %zu\n", size));
|
||||||
|
|
||||||
|
/* Truncate large packets. */
|
||||||
|
if (size > max)
|
||||||
|
size = max;
|
||||||
|
|
||||||
/* Copy out the packet. */
|
/* Copy out the packet. */
|
||||||
off = 0;
|
netdriver_copyout(data, 0, rxd->data, size);
|
||||||
left = count;
|
|
||||||
pos = rxd->data;
|
|
||||||
|
|
||||||
for (i = 0; i < m->m_net_netdrv_dl_readv_s.count && left > 0;
|
|
||||||
i += batch) {
|
|
||||||
/* Copy in the next batch. */
|
|
||||||
batch = MIN(m->m_net_netdrv_dl_readv_s.count - i, NR_IOREQS);
|
|
||||||
|
|
||||||
r = sys_safecopyfrom(m->m_source,
|
|
||||||
m->m_net_netdrv_dl_readv_s.grant, off, (vir_bytes)iovec,
|
|
||||||
batch * sizeof(iovec[0]));
|
|
||||||
if (r != OK)
|
|
||||||
panic("vector copy failed: %d", r);
|
|
||||||
|
|
||||||
/* Copy out each element in the batch, until we run out. */
|
|
||||||
for (j = 0, iovp = iovec; j < batch && left > 0; j++, iovp++) {
|
|
||||||
size = MIN(iovp->iov_size, left);
|
|
||||||
|
|
||||||
r = sys_safecopyto(m->m_source, iovp->iov_grant, 0,
|
|
||||||
(vir_bytes)pos, size);
|
|
||||||
if (r != OK)
|
|
||||||
panic("safe copy failed: %d", r);
|
|
||||||
|
|
||||||
pos += size;
|
|
||||||
left -= size;
|
|
||||||
}
|
|
||||||
|
|
||||||
off += batch * sizeof(iovec[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not sure what to do here. Inet shouldn't mess this up anyway. */
|
|
||||||
if (left > 0) {
|
|
||||||
printf("ATL2: truncated packet of %d bytes by %d bytes\n",
|
|
||||||
count, left);
|
|
||||||
count -= left;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We are done with this packet. Move on to the next. */
|
/* We are done with this packet. Move on to the next. */
|
||||||
atl2_rx_advance(TRUE /*next*/);
|
atl2_rx_advance(TRUE /*next*/);
|
||||||
|
|
||||||
/* We have now successfully received a packet. */
|
return size;
|
||||||
state.flags &= ~ATL2_FLAG_READ_PEND;
|
|
||||||
state.flags |= ATL2_FLAG_PACK_RCVD;
|
|
||||||
state.recv_count = count;
|
|
||||||
|
|
||||||
/* If called from the interrupt handler, the caller will reply. */
|
|
||||||
if (!from_int)
|
|
||||||
atl2_reply();
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
suspend:
|
|
||||||
/*
|
|
||||||
* No packets are available at this time. If we were not already
|
|
||||||
* trying to resume receipt, save the read request for later, and tell
|
|
||||||
* Inet that the request has been suspended.
|
|
||||||
*/
|
|
||||||
if (from_int)
|
|
||||||
return;
|
|
||||||
|
|
||||||
state.flags |= ATL2_FLAG_READ_PEND;
|
|
||||||
state.read_msg = *m;
|
|
||||||
|
|
||||||
atl2_reply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write packet data.
|
* Send a packet.
|
||||||
*/
|
*/
|
||||||
static void
|
static int
|
||||||
atl2_writev(const message * m, int from_int)
|
atl2_send(struct netdriver_data * data, size_t size)
|
||||||
{
|
{
|
||||||
iovec_s_t *iovp;
|
size_t pos, chunk;
|
||||||
size_t off, count, left, pos, skip;
|
|
||||||
vir_bytes size;
|
|
||||||
uint8_t *sizep;
|
uint8_t *sizep;
|
||||||
int i, j, r, batch, maxnum;
|
|
||||||
|
|
||||||
/* We can deal with only one write request from Inet at a time. */
|
|
||||||
assert(from_int || !(state.flags & ATL2_FLAG_WRITE_PEND));
|
|
||||||
|
|
||||||
state.task_endpt = m->m_source;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we are already certain that the packet won't fit, bail out.
|
* If the packet won't fit, bail out. Keep at least some space between
|
||||||
* Keep at least some space between TxD head and tail, as it is not
|
* TxD head and tail, as it is not clear whether the device deals well
|
||||||
* clear whether the device deals well with the case that they collide.
|
* with the case that they collide.
|
||||||
*/
|
*/
|
||||||
if (state.txs_num >= ATL2_TXS_COUNT)
|
if (state.txs_num >= ATL2_TXS_COUNT)
|
||||||
goto suspend;
|
return SUSPEND;
|
||||||
maxnum = ATL2_TXD_BUFSIZE - ETH_MIN_PACK_SIZE - sizeof(uint32_t);
|
|
||||||
if (state.txd_num >= maxnum)
|
|
||||||
goto suspend;
|
|
||||||
|
|
||||||
/*
|
if (state.txd_num + sizeof(uint32_t) + ATL2_ALIGN_32(size) >=
|
||||||
* Optimistically try to copy in the data; suspend if it turns out
|
ATL2_TXD_BUFSIZE)
|
||||||
* that it does not fit.
|
return SUSPEND;
|
||||||
*/
|
|
||||||
off = 0;
|
/* Copy in the packet. */
|
||||||
count = 0;
|
|
||||||
left = state.txd_num - sizeof(uint32_t);
|
|
||||||
pos = (state.txd_tail + state.txd_num +
|
pos = (state.txd_tail + state.txd_num +
|
||||||
sizeof(uint32_t)) % ATL2_TXD_BUFSIZE;
|
sizeof(uint32_t)) % ATL2_TXD_BUFSIZE;
|
||||||
|
chunk = ATL2_TXD_BUFSIZE - pos;
|
||||||
for (i = 0; i < m->m_net_netdrv_dl_writev_s.count; i += batch) {
|
if (size > chunk) {
|
||||||
/* Copy in the next batch. */
|
netdriver_copyin(data, 0, state.txd_base + pos, chunk);
|
||||||
batch = MIN(m->m_net_netdrv_dl_writev_s.count - i, NR_IOREQS);
|
netdriver_copyin(data, chunk, state.txd_base, size - chunk);
|
||||||
|
} else
|
||||||
r = sys_safecopyfrom(m->m_source,
|
netdriver_copyin(data, 0, state.txd_base + pos, size);
|
||||||
m->m_net_netdrv_dl_writev_s.grant, off, (vir_bytes)iovec,
|
|
||||||
batch * sizeof(iovec[0]));
|
|
||||||
if (r != OK)
|
|
||||||
panic("vector copy failed: %d", r);
|
|
||||||
|
|
||||||
/* Copy in each element in the batch. */
|
|
||||||
for (j = 0, iovp = iovec; j < batch; j++, iovp++) {
|
|
||||||
size = iovp->iov_size;
|
|
||||||
if (size > left)
|
|
||||||
goto suspend;
|
|
||||||
|
|
||||||
skip = 0;
|
|
||||||
if (size > ATL2_TXD_BUFSIZE - pos) {
|
|
||||||
skip = ATL2_TXD_BUFSIZE - pos;
|
|
||||||
r = sys_safecopyfrom(m->m_source,
|
|
||||||
iovp->iov_grant, 0,
|
|
||||||
(vir_bytes)(state.txd_base + pos), skip);
|
|
||||||
if (r != OK)
|
|
||||||
panic("safe copy failed: %d", r);
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = sys_safecopyfrom(m->m_source, iovp->iov_grant,
|
|
||||||
skip, (vir_bytes)(state.txd_base + pos),
|
|
||||||
size - skip);
|
|
||||||
if (r != OK)
|
|
||||||
panic("safe copy failed: %d", r);
|
|
||||||
|
|
||||||
pos = (pos + size - skip) % ATL2_TXD_BUFSIZE;
|
|
||||||
left -= size;
|
|
||||||
count += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
off += batch * sizeof(iovec[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(count <= ETH_MAX_PACK_SIZE_TAGGED);
|
|
||||||
|
|
||||||
/* Write the length to the DWORD right before the packet. */
|
/* Write the length to the DWORD right before the packet. */
|
||||||
sizep = state.txd_base +
|
sizep = state.txd_base +
|
||||||
(state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE;
|
(state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE;
|
||||||
*(uint32_t *)sizep = count;
|
*(volatile uint32_t *)sizep = size;
|
||||||
|
|
||||||
/* Update the TxD head. */
|
/* Update the TxD head. */
|
||||||
state.txd_num += sizeof(uint32_t) + ATL2_ALIGN_32(count);
|
state.txd_num += sizeof(uint32_t) + ATL2_ALIGN_32(size);
|
||||||
pos = ATL2_ALIGN_32(pos) % ATL2_TXD_BUFSIZE;
|
pos = ATL2_ALIGN_32(pos + size) % ATL2_TXD_BUFSIZE;
|
||||||
assert((int)pos ==
|
assert((int)pos ==
|
||||||
(state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE);
|
(state.txd_tail + state.txd_num) % ATL2_TXD_BUFSIZE);
|
||||||
|
|
||||||
@ -928,66 +793,44 @@ atl2_writev(const message * m, int from_int)
|
|||||||
|
|
||||||
ATL2_WRITE_U32(ATL2_TXD_IDX_REG, pos / sizeof(uint32_t));
|
ATL2_WRITE_U32(ATL2_TXD_IDX_REG, pos / sizeof(uint32_t));
|
||||||
|
|
||||||
/* We have now successfully set up the transmission of a packet. */
|
return OK;
|
||||||
state.flags &= ~ATL2_FLAG_WRITE_PEND;
|
|
||||||
state.flags |= ATL2_FLAG_PACK_SENT;
|
|
||||||
|
|
||||||
/* If called from the interrupt handler, the caller will reply. */
|
|
||||||
if (!from_int)
|
|
||||||
atl2_reply();
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
suspend:
|
|
||||||
/*
|
|
||||||
* We cannot transmit the packet at this time. If we were not already
|
|
||||||
* trying to resume transmission, save the write request for later,
|
|
||||||
* and tell Inet that the request has been suspended.
|
|
||||||
*/
|
|
||||||
if (from_int)
|
|
||||||
return;
|
|
||||||
|
|
||||||
state.flags |= ATL2_FLAG_WRITE_PEND;
|
|
||||||
state.write_msg = *m;
|
|
||||||
|
|
||||||
atl2_reply();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process an interrupt.
|
* Process an interrupt.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
atl2_intr(const message * __unused m)
|
atl2_intr(unsigned int __unused mask)
|
||||||
{
|
{
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
int r, try_write, try_read;
|
int r, try_send, try_recv;
|
||||||
|
|
||||||
/* Clear and disable interrupts. */
|
/* Clear and disable interrupts. */
|
||||||
val = ATL2_READ_U32(ATL2_ISR_REG);
|
val = ATL2_READ_U32(ATL2_ISR_REG);
|
||||||
|
|
||||||
ATL2_WRITE_U32(ATL2_ISR_REG, val | ATL2_ISR_DISABLE);
|
ATL2_WRITE_U32(ATL2_ISR_REG, val | ATL2_ISR_DISABLE);
|
||||||
|
|
||||||
ATL2_DEBUG(("ATL2: interrupt (0x%08lx)\n", val));
|
ATL2_DEBUG(("ATL2: interrupt (0x%08x)\n", val));
|
||||||
|
|
||||||
/* If an error occurred, reset the card. */
|
/* If an error occurred, reset the card. */
|
||||||
if (val & (ATL2_ISR_DMAR_TIMEOUT | ATL2_ISR_DMAW_TIMEOUT |
|
if (val & (ATL2_ISR_DMAR_TIMEOUT | ATL2_ISR_DMAW_TIMEOUT |
|
||||||
ATL2_ISR_PHY_LINKDOWN))
|
ATL2_ISR_PHY_LINKDOWN))
|
||||||
atl2_setup();
|
atl2_setup();
|
||||||
|
|
||||||
try_write = try_read = FALSE;
|
try_send = try_recv = FALSE;
|
||||||
|
|
||||||
/* Process sent data, and possibly send pending data. */
|
/* Process sent data, and possibly send pending data. */
|
||||||
if (val & ATL2_ISR_TX_EVENT) {
|
if (val & ATL2_ISR_TX_EVENT) {
|
||||||
if (atl2_tx_advance())
|
if (atl2_tx_advance())
|
||||||
try_write = (state.flags & ATL2_FLAG_WRITE_PEND);
|
try_send = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive new data, and possible satisfy a pending receive request. */
|
/* Receive new data, and possible satisfy a pending receive request. */
|
||||||
if (val & ATL2_ISR_RX_EVENT) {
|
if (val & ATL2_ISR_RX_EVENT) {
|
||||||
if (!(state.flags & ATL2_FLAG_RX_AVAIL)) {
|
if (!state.rx_avail) {
|
||||||
atl2_rx_advance(FALSE /*next*/);
|
atl2_rx_advance(FALSE /*next*/);
|
||||||
|
|
||||||
try_read = (state.flags & ATL2_FLAG_READ_PEND);
|
try_recv = TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,60 +840,21 @@ atl2_intr(const message * __unused m)
|
|||||||
if ((r = sys_irqenable(&state.hook_id)) != OK)
|
if ((r = sys_irqenable(&state.hook_id)) != OK)
|
||||||
panic("unable to enable IRQ: %d", r);
|
panic("unable to enable IRQ: %d", r);
|
||||||
|
|
||||||
/* Attempt to satisfy pending write and read requests. */
|
/* Attempt to satisfy pending send and receive requests. */
|
||||||
if (try_write)
|
if (try_send)
|
||||||
atl2_writev(&state.write_msg, TRUE /*from_int*/);
|
netdriver_send();
|
||||||
if (try_read)
|
if (try_recv)
|
||||||
atl2_readv(&state.read_msg, TRUE /*from_int*/);
|
netdriver_recv();
|
||||||
if (state.flags & (ATL2_FLAG_PACK_SENT | ATL2_FLAG_PACK_RCVD))
|
|
||||||
atl2_reply();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Configure the mode of the card.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
atl2_conf(message * m)
|
|
||||||
{
|
|
||||||
ether_addr_t addr;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
state.mode = m->m_net_netdrv_dl_conf.mode;
|
|
||||||
|
|
||||||
atl2_set_mode();
|
|
||||||
|
|
||||||
addr.ea_addr[0] = state.hwaddr[1] >> 8;
|
|
||||||
addr.ea_addr[1] = state.hwaddr[1] & 0xff;
|
|
||||||
addr.ea_addr[2] = state.hwaddr[0] >> 24;
|
|
||||||
addr.ea_addr[3] = (state.hwaddr[0] >> 16) & 0xff;
|
|
||||||
addr.ea_addr[4] = (state.hwaddr[0] >> 8) & 0xff;
|
|
||||||
addr.ea_addr[5] = state.hwaddr[0] & 0xff;
|
|
||||||
|
|
||||||
memcpy(m->m_netdrv_net_dl_conf.hw_addr, &addr,
|
|
||||||
sizeof(m->m_netdrv_net_dl_conf.hw_addr));
|
|
||||||
|
|
||||||
m->m_type = DL_CONF_REPLY;
|
|
||||||
m->m_netdrv_net_dl_conf.stat = OK;
|
|
||||||
|
|
||||||
if ((r = ipc_send(m->m_source, m)) != OK)
|
|
||||||
printf("ATL2: unable to send reply (%d)\n", r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy out statistics.
|
* Copy out statistics.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
atl2_getstat(message * m)
|
atl2_stat(eth_stat_t * stat)
|
||||||
{
|
{
|
||||||
int r;
|
|
||||||
|
|
||||||
sys_safecopyto(m->m_source, m->m_net_netdrv_dl_getstat_s.grant, 0,
|
memcpy(stat, &state.stat, sizeof(*stat));
|
||||||
(vir_bytes) &state.stat, sizeof(state.stat));
|
|
||||||
|
|
||||||
m->m_type = DL_STAT_REPLY;
|
|
||||||
|
|
||||||
if ((r = ipc_send(m->m_source, m)) != OK)
|
|
||||||
printf("ATL2: unable to send reply (%d)\n", r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1127,65 +931,70 @@ atl2_dump(void)
|
|||||||
printf("TxS tail: %8d\t", state.txs_tail);
|
printf("TxS tail: %8d\t", state.txs_tail);
|
||||||
printf("TxS count: %8d\n", state.txs_num);
|
printf("TxS count: %8d\n", state.txs_num);
|
||||||
|
|
||||||
printf("flags: 0x%04x\t", state.flags);
|
|
||||||
atl2_dump_link();
|
atl2_dump_link();
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process miscellaneous messages.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
atl2_other(const message * m_ptr, int ipc_status)
|
||||||
|
{
|
||||||
|
#if ATL2_FKEY
|
||||||
|
int sfkeys;
|
||||||
|
|
||||||
|
if (!is_ipc_notify(ipc_status) || m_ptr->m_source != TTY_PROC_NR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (fkey_events(NULL, &sfkeys) == OK && bit_isset(sfkeys, ATL2_FKEY))
|
||||||
|
atl2_dump();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the atl2 driver.
|
* Initialize the atl2 driver.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
sef_cb_init_fresh(int __unused type, sef_init_info_t * __unused info)
|
atl2_init(unsigned int instance, ether_addr_t * addr)
|
||||||
{
|
{
|
||||||
int r, devind, instance;
|
int devind;
|
||||||
long v;
|
|
||||||
#if ATL2_FKEY
|
#if ATL2_FKEY
|
||||||
int fkeys, sfkeys;
|
int r, fkeys, sfkeys;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* How many matching devices should we skip? */
|
memset(&state, 0, sizeof(state));
|
||||||
v = 0;
|
|
||||||
(void)env_parse("instance", "d", 0, &v, 0, 255);
|
|
||||||
instance = (int)v;
|
|
||||||
|
|
||||||
/* Try to find a recognized device. */
|
/* Try to find a recognized device. */
|
||||||
devind = atl2_probe(instance);
|
devind = atl2_probe(instance);
|
||||||
|
|
||||||
if (devind < 0)
|
if (devind < 0)
|
||||||
panic("no matching device found");
|
return ENXIO;
|
||||||
|
|
||||||
/* Initialize the device. */
|
/* Initialize the device. */
|
||||||
atl2_init(devind);
|
atl2_init_hw(devind, addr);
|
||||||
|
|
||||||
/* Announce we are up! */
|
|
||||||
netdriver_announce();
|
|
||||||
|
|
||||||
#if ATL2_FKEY
|
#if ATL2_FKEY
|
||||||
/* Register debug dump function key. */
|
/* Register debug dump function key. */
|
||||||
fkeys = sfkeys = 0;
|
fkeys = sfkeys = 0;
|
||||||
bit_set(sfkeys, 11);
|
bit_set(sfkeys, ATL2_FKEY);
|
||||||
if ((r = fkey_map(&fkeys, &sfkeys)) != OK)
|
if ((r = fkey_map(&fkeys, &sfkeys)) != OK)
|
||||||
printf("ATL2: warning, could not map Shift+F11 key (%d)\n", r);
|
printf("ATL2: warning, could not map Shift+F%u key (%d)\n",
|
||||||
|
r, ATL2_FKEY);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/*
|
/*
|
||||||
* In case of a termination signal, shut down this driver. Stop the device,
|
* Deallocate resources as proof of concept. Currently unused.
|
||||||
* and deallocate resources as proof of concept.
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
sef_cb_signal_handler(int signo)
|
atl2_cleanup(void)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
/* Only check for termination signal, ignore anything else. */
|
|
||||||
if (signo != SIGTERM) return;
|
|
||||||
|
|
||||||
atl2_stop();
|
|
||||||
|
|
||||||
if ((r = sys_irqrmpolicy(&state.hook_id)) != OK)
|
if ((r = sys_irqrmpolicy(&state.hook_id)) != OK)
|
||||||
panic("unable to deregister IRQ: %d", r);
|
panic("unable to deregister IRQ: %d", r);
|
||||||
|
|
||||||
@ -1197,32 +1006,8 @@ sef_cb_signal_handler(int signo)
|
|||||||
vm_unmap_phys(SELF, (void *)state.base, state.size);
|
vm_unmap_phys(SELF, (void *)state.base, state.size);
|
||||||
|
|
||||||
/* We cannot free the PCI device at this time. */
|
/* We cannot free the PCI device at this time. */
|
||||||
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Perform SEF initialization.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
sef_local_startup(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Register init callbacks. */
|
|
||||||
sef_setcb_init_fresh(sef_cb_init_fresh);
|
|
||||||
sef_setcb_init_lu(sef_cb_init_fresh);
|
|
||||||
sef_setcb_init_restart(sef_cb_init_fresh);
|
|
||||||
|
|
||||||
/* Register live update callbacks. */
|
|
||||||
sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
|
|
||||||
sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_workfree);
|
|
||||||
|
|
||||||
/* Register signal callbacks. */
|
|
||||||
sef_setcb_signal_handler(sef_cb_signal_handler);
|
|
||||||
|
|
||||||
/* Let SEF perform startup. */
|
|
||||||
sef_startup();
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The ATL2 ethernet driver.
|
* The ATL2 ethernet driver.
|
||||||
@ -1230,47 +1015,10 @@ sef_local_startup(void)
|
|||||||
int
|
int
|
||||||
main(int argc, char ** argv)
|
main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
message m;
|
|
||||||
int ipc_status;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
/* Initialize SEF. */
|
|
||||||
env_setargs(argc, argv);
|
env_setargs(argc, argv);
|
||||||
sef_local_startup();
|
|
||||||
|
|
||||||
for (;;) {
|
netdriver_task(&atl2_table);
|
||||||
if ((r = netdriver_receive(ANY, &m, &ipc_status)) != OK)
|
|
||||||
panic("netdriver_receive failed: %d", r);
|
|
||||||
|
|
||||||
if (is_ipc_notify(ipc_status)) {
|
return EXIT_SUCCESS;
|
||||||
switch (m.m_source) {
|
|
||||||
case HARDWARE: /* interrupt */
|
|
||||||
atl2_intr(&m);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TTY_PROC_NR: /* function key */
|
|
||||||
atl2_dump();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
printf("ATL2: illegal notify from %d\n",
|
|
||||||
m.m_source);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process requests from Inet. */
|
|
||||||
switch (m.m_type) {
|
|
||||||
case DL_CONF: atl2_conf(&m); break;
|
|
||||||
case DL_GETSTAT_S: atl2_getstat(&m); break;
|
|
||||||
case DL_WRITEV_S: atl2_writev(&m, FALSE); break;
|
|
||||||
case DL_READV_S: atl2_readv(&m, FALSE); break;
|
|
||||||
default:
|
|
||||||
printf("ATL2: illegal message %d from %d\n",
|
|
||||||
m.m_type, m.m_source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user