From 80fca204e6207f2259e1102110ce292ddb064fe3 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 7 Sep 2018 02:30:26 +0100 Subject: [PATCH] net_wiznet: initial Wiznet 5100 support Some elements of the code come under the category of 'sufficiently rigged demo' but as a basis it seems to be workable now. --- Kernel/dev/net/net_wiznet.c | 379 +++++++++++++++++++++++++++--------- Kernel/dev/net/net_wiznet.h | 3 + 2 files changed, 288 insertions(+), 94 deletions(-) create mode 100644 Kernel/dev/net/net_wiznet.h diff --git a/Kernel/dev/net/net_wiznet.c b/Kernel/dev/net/net_wiznet.c index cf007a09..382f86a2 100644 --- a/Kernel/dev/net/net_wiznet.c +++ b/Kernel/dev/net/net_wiznet.c @@ -11,74 +11,195 @@ socket and when it connects allocate another one. That means we need to use to break the 1:1 mapping between wiznet and OS socket numbering - handle address collision interrupt + - rework disconnect state machine */ +#include +#include +#include +#include +#include + +#ifdef CONFIG_NET_WIZNET + static uint8_t irqmask; -#define RX_MASK 0x1FFF -#define TX_MASK 0x1FFF +#define RX_MASK 0x07FF +#define TX_MASK 0x07FF + +#define MR 0x0000 +#define MR_RESET 0x80 +#define MR_PB 0x10 +#define MR_PPPOE 0x08 +#define MR_AUTOINC 0x02 +#define MR_INDIRECT 0x01 + +#define GAR0 0x0001 +#define SUBR0 0x0005 +#define SHAR0 0x0009 +#define SIPR0 0x000F +#define IR 0x0015 +#define IMR 0x0016 +#define RTR0 0x0017 +#define RCR 0x0019 +#define RMSR 0x001A +#define TMSR 0x001B +#define PATR0 0x001C +#define PTIMER 0x0028 +#define PMAGIC 0x0029 +#define UIPR0 0x002A +#define UPORT0 0x002E + +#define Sn_MR 0x0400 +#define Sn_CR 0x0401 +#define OPEN 0x01 +#define LISTEN 0x02 +#define CONNECT 0x04 +#define DISCON 0x08 +#define CLOSE 0x10 +#define SEND 0x20 +#define SEND_MAC 0x21 +#define SEND_KEEP 0x22 +#define RECV 0x40 +#define Sn_IR 0x0402 +#define I_SEND_OK 0x10 +#define I_TIMEOUT 0x08 +#define I_RECV 0x04 +#define I_DISCON 0x02 +#define I_CON 0x01 +#define Sn_SR 0x0403 +#define SOCK_CLOSED 0x00 +#define SOCK_ARP 0x01 +#define SOCK_INIT 0x13 +#define SOCK_LISTEN 0x14 +#define SOCK_SYNSENT 0x15 +#define SOCK_SYNRECV 0x16 +#define SOCK_ESTABLISHED 0x17 +#define SOCK_FIN_WAIT 0x18 +#define SOCK_CLOSING 0x1A +#define SOCK_TIME_WAIT 0x1B +#define SOCK_CLOSE_WAIT 0x1C +#define SOCK_LAST_ACK 0x1D +#define SOCK_UDP 0x22 +#define SOCK_IPRAW 0x32 +#define SOCK_MACROW 0x42 +#define SOC_PPPOE 0x5F +#define Sn_PORT0 0x0404 +#define Sn_DHAR0 0x0406 +#define Sn_DIPR0 0x040C +#define Sn_DPORT0 0x0410 +#define Sn_MSSR0 0x0412 +#define Sn_PROTO 0x0414 +#define Sn_TOS 0x0415 +#define Sn_TTL 0x0416 +#define Sn_TX_FSR 0x0420 +#define Sn_TX_RD0 0x0422 +#define Sn_TX_WR0 0x0424 +#define Sn_RX_RSR 0x0426 +#define Sn_RX_RD0 0x0428 + + + /* Core helpers: platform supplies wiz_bread{_u} and wiz_bwrite{_u} */ -/* Might be worth inlining these on some platforms - need to think about - that carefully */ +__sfr __at 0x28 mr; +__sfr __at 0x29 idm_ar0; +__sfr __at 0x2A idm_ar1; +__sfr __at 0x2B idm_dr; -static uint8_t wiz_readb(uint16_t offset) +/* We assume indirect, autoinc is always set */ +static uint8_t wiz_readb(uint16_t off) { - uint8_t n; - wiz_bread(offset, &n, 1); - return n; + idm_ar0 = off >> 8; + idm_ar1 = off; + return idm_dr; } -static uint16_t wiz_readw(uint16_t offset) +static uint16_t wiz_readw(uint16_t off) { uint16_t n; - wiz_bread(offset, &n, 2); + idm_ar0 = off >> 8; + idm_ar1 = off; + n = ((uint16_t)idm_dr) << 8; + n |= idm_dr; return n; } -static uint32_t wiz_readl(uint16_t offset) +static uint32_t wiz_readl(uint16_t off) { uint32_t n; - wiz_bread(offset, &n, 4); + idm_ar0 = off >> 8; + idm_ar1 = off; + n = ((uint32_t)idm_dr) << 24; + n |= ((uint32_t)idm_dr) << 16; + n |= ((uint16_t)idm_dr) << 8; + n |= idm_dr; return n; } -static void wiz_writeb(uint16_t offset, uint8_t n) +static void wiz_bread(uint16_t off, uint8_t *p, uint8_t n) { - wiz_bwrite(offset, &n, 1); + idm_ar0 = off >> 8; + idm_ar1 = off; + while(n--) + *p++ = idm_dr; } -static void wiz_writew(uint16_t offset, uint16_t n) +static void wiz_breadu(uint16_t off, uint8_t *p, uint8_t n) { - wiz_bwrite(offset, &n, 2); + idm_ar0 = off >> 8; + idm_ar1 = off; + while(n--) + uputc(idm_dr, p++); } -#ifdef BIG_ENDIAN -#define wiz_writew_n(a,b) wiz_writew(a,b) -#define wiz_readw_n(a) wiz_readw(a) -#else +static void wiz_writeb(uint16_t off, uint8_t n) +{ + idm_ar0 = off >> 8; + idm_ar1 = off; + idm_dr = n; +} -static void wiz_writew_n(uint16_t offset, uint16_t n) +static void wiz_writew(uint16_t off, uint16_t n) { - n = htons(n); - wiz_bwrite(offset, &n, 2); + idm_ar0 = off >> 8; + idm_ar1 = off; + idm_dr = n >> 8; + idm_dr = n; } -static uint16_t wiz_readw_n(uint16_t offset) +static void wiz_writel(uint16_t off, uint32_t n) { - uint16_t n; - wiz_bread(offset, &n, 2); - return ntohs(n); + idm_ar0 = off >> 8; + idm_ar1 = off; + idm_dr = n >> 24; + idm_dr = n >> 16; + idm_dr = n >> 8; + idm_dr = n; } -#endif +static void wiz_bwrite(uint16_t off, uint8_t *p, uint8_t n) +{ + idm_ar0 = off >> 8; + idm_ar1 = off; + while(n--) + idm_dr = *p++; +} + +static void wiz_bwriteu(uint16_t off, uint8_t *p, uint8_t n) +{ + idm_ar0 = off >> 8; + idm_ar1 = off; + while(n--) + idm_dr = ugetc(p++); +} /* FIXME: look for ways to fold these four together */ static void wiz_queue(uint16_t i, uint16_t n, uint8_t * p) { uint16_t dm = wiz_readw(Sn_TX_WR0 + i) & TX_MASK; - uint16_t tx_base = 0x8000 + (i << 3); /* i is already << 8 */ + uint16_t tx_base = 0x4000 + (i << 3); /* i is already << 8 */ if (dm + n >= TX_MASK) { uint16_t us = TX_MASK + 1 - dm; @@ -90,21 +211,21 @@ static void wiz_queue(uint16_t i, uint16_t n, uint8_t * p) static void wiz_queue_u(uint16_t i, uint16_t n, uint8_t * p) { - uint16_t dm = wiz_readw_n(Sn_TX_WR0 + i) & TX_MASK; - uint16_t tx_base = 0x8000 + (i << 3); /* i is already << 8 */ + uint16_t dm = wiz_readw(Sn_TX_WR0 + i) & TX_MASK; + uint16_t tx_base = 0x4000 + (i << 3); /* i is already << 8 */ if (dm + n >= TX_MASK) { uint16_t us = TX_MASK + 1 - dm; - wiz_bwrite_u(dm + tx_base, p, us); - wiz_bwrite_u(tx_base, p + us, n - us); + wiz_bwriteu(dm + tx_base, p, us); + wiz_bwriteu(tx_base, p + us, n - us); } else - wiz_bwrite_u(dm + tx_base, p, n); + wiz_bwriteu(dm + tx_base, p, n); } static void wiz_dequeue(uint16_t i, uint16_t n, uint8_t * p) { - uint16_t dm = wiz_readw_n(Sn_RX0 + i) & RX_MASK; - uint16_t rx_base = 0xC000 + (i << 3); /* i is already << 8 */ + uint16_t dm = wiz_readw(Sn_RX_RD0 + i) & RX_MASK; + uint16_t rx_base = 0x6000 + (i << 3); /* i is already << 8 */ if (dm + n >= RX_MASK) { uint16_t us = RX_MASK + 1 - dm; @@ -116,15 +237,15 @@ static void wiz_dequeue(uint16_t i, uint16_t n, uint8_t * p) static void wiz_dequeue_u(uint16_t i, uint16_t n, uint8_t * p) { - uint16_t dm = wiz_readw_n(Sn_RX0 + i) & RX_MASK; - uint16_t rx_base = 0xC000 + (i << 3); /* i is already << 8 */ + uint16_t dm = wiz_readw(Sn_RX_RD0 + i) & RX_MASK; + uint16_t rx_base = 0x6000 + (i << 3); /* i is already << 8 */ if (dm + n >= RX_MASK) { uint16_t us = RX_MASK + 1 - dm; - wiz_bread_u(dm + rx_base, p, us); - wiz_bread_u(rx_base, p + us, n - us); + wiz_breadu(dm + rx_base, p, us); + wiz_breadu(rx_base, p + us, n - us); } else - wiz_bread_u(dm + rx_base, p, n); + wiz_breadu(dm + rx_base, p, n); } static void wiz_wakeall(struct socket *s) @@ -143,18 +264,27 @@ static void wiz_eof(struct socket *s) /* * Process interrupts from the WizNet device */ -void wiz_event_s(uint8_t i) +static void wiz_event_s(uint8_t i) { struct socket *s = &sockets[i]; uint16_t stat = wiz_readw(Sn_IR + (i << 8)); /* BE read of reg pair */ - if (stat & 0x800) { + if (stat & 0x1000) { /* Transmit completed: window re-open. We can allow more data to flow from the user */ s->s_iflag &= ~SI_THROTTLE; - wiz_writeb(Sn_IR + (i << 8), 0x08); /* Clear the flag down */ + wiz_writeb(Sn_IR + (i << 8), 0x10); /* Clear the flag down */ wakeup(&s->s_data); } + if (stat & 0x800) { + /* Timeout */ + s->s_error = ETIMEDOUT; + wiz_writeb(Sn_CR + (i << 8), CLOSE); + wiz_wakeall(s); + wiz_writeb(Sn_IR + (i << 8), 0x08); + wiz_eof(s); + /* Fall through and let CLOSE state processing do the work */ + } if (stat & 0x400) { /* Receive wake: Poke the user in case they are reading */ s->s_iflag |= SI_DATA; @@ -164,8 +294,11 @@ void wiz_event_s(uint8_t i) if (stat & 0x200) { /* Disconnect: Just kill our host socket. Not clear if this is right or we need to drain data first */ - s->state = SS_CLOSED; + wiz_writeb(Sn_IR + (i << 8), 0x02); /* Clear the flag down */ + wiz_writeb(Sn_CR + (i << 8), CLOSE); wiz_eof(s); + /* When we fall through we'll see CLOSE state and do the + actual shutting down */ } if (stat & 0x100) { /* Connect: Move into connected state */ @@ -173,20 +306,26 @@ void wiz_event_s(uint8_t i) s->s_state = SS_CONNECTED; wakeup(s); } + wiz_writeb(Sn_IR + (i << 8), 0x01); /* Clear the flag down */ } /* ??? return if high bits set here ?? */ switch (stat & 0xFF) { case 0: /* SOCK_CLOSED */ if (s->s_state != SS_CLOSED && s->s_state != SS_UNUSED) { - s->s_state = SS_CLOSED; - if (s->s_state != SS_CLOSING) { + if (s->s_state != SS_CLOSING && s->s_state != SS_DEAD) { s->s_error = ECONNRESET; /* Sort of a guess */ wiz_wakeall(s); } else wakeup(s); + irqmask &= ~(1 << i); + wiz_writeb(IMR, irqmask); + wiz_eof(s); + /* Net layer wants us to burn the socket */ + if (s->s_state == SS_DEAD) + sock_closed(s); + else /* so net_close() burns the socket */ + s->s_state = SS_CLOSED; } - irqmask &= ~(1 << i); - wiz_writeb(Sn_IMR, irqmask); break; case 0x13: /* SOCK_INIT */ break; @@ -196,7 +335,7 @@ void wiz_event_s(uint8_t i) if (s->s_state == SS_CONNECTING) { s->s_state == SS_CONNECTED; wakeup(s); - } else if (s->s_state == SS_LISTEN) { + } else if (s->s_state == SS_LISTENING) { /* TODO: We actually have to split the association between wiznet and host sockets as we have multiple wiznet sockets in the accept queue and a listener that is not really a @@ -206,7 +345,7 @@ void wiz_event_s(uint8_t i) case 0x1C: /* SOCK_CLOSE_WAIT */ if (s->s_state == SS_CONNECTED || s->s_state == SS_CONNECTING) - s->s_state == SS_CLOSEWAIT; + s->s_state = SS_CLOSEWAIT; wiz_eof(s); if (s->s_state == SS_ACCEPTWAIT) { /* HUM ??? */ @@ -226,10 +365,11 @@ void wiz_event(void) { uint8_t irq; uint8_t i = 0; + struct socket *s = sockets; /* Polling cases */ - irq = wiz_readb(IR2); + irq = wiz_readb(IR) & 0x0F; if (irq == 0) return; @@ -261,30 +401,35 @@ int net_bind(struct socket *s) { uint16_t i = s - sockets; uint8_t r = SOCK_INIT; + uint16_t off = i << 8; - switch (s->sock_type) { + switch (s->s_type) { case SOCKTYPE_TCP: - wiz_writeb(Sn_MR + (i << 8), 0x01); /* TCP */ - wiz_writew(Sn_PORT0 + (i << 8), s->s_addr[SADDR_SRC].port); + wiz_writeb(Sn_MR + off, 0x21); /* TCP, delayed ack */ + /* We keep ports net endian so don't byte swap */ + wiz_bwrite(Sn_PORT0 + off, &s->s_addr[SADDR_SRC].port, 2); break; case SOCKTYPE_UDP: - wiz_writeb(Sn_MR + (i << 8), 0x02); /* UDP */ - wiz_writew(Sn_PORT0 + (i << 8), s->s_addr[SADDR_SRC].port); + wiz_writeb(Sn_MR + off, 0x02); /* UDP */ + wiz_bwrite(Sn_PORT0 + off, &s->s_addr[SADDR_SRC].port, 2); r = SOCK_UDP; break; case SOCKTYPE_RAW: - wiz_writeb(Sn_PROTO + (i << 8), s->s_addr[ADDR_SRC].port); /* hack */ - wiz_writeb(Sn_MR + (i << 8), 0x03); /* RAW */ + wiz_writeb(Sn_PROTO + off, s->s_addr[SADDR_SRC].port); /* hack */ + wiz_writeb(Sn_MR + off, 0x03); /* RAW */ r = SOCK_IPRAW; } - wiz_writeb(Sn_CR + (i << 8), OPEN); + /* Make an open request to open the socket */ + wiz_writeb(Sn_CR + off, OPEN); - if (wiz_readb(Sn_SR + (i << 8)) != r) { + /* If the reply is not immediately SOCK_INT we failed */ + if (wiz_readb(Sn_SR + off) != r) { udata.u_error = EADDRINUSE; /* Something broke ? */ return -1; } + /* Interrupt on if available mark as bound */ irqmask |= 1 << i; - wiz_writeb(Sn_IMR, irqmask); + wiz_writeb(IMR, irqmask); s->s_state = SS_BOUND; /* Do we need to delay the SS_BOUND until the chip interrupts ? */ return 0; @@ -303,9 +448,10 @@ int net_listen(struct socket *s) i <<= 8; + /* Issue a listen command. Check the state went to SOCK_LISTEN */ wiz_writeb(Sn_CR + i, LISTEN); if (wiz_readb(Sn_SR + i) != SOCK_LISTEN) { - udata.u_error = EPROTO; /* ??? */ + udata.u_error = EIO;//FIXME EPROTO; /* ??? */ return -1; } s->s_state = SS_LISTENING; @@ -322,9 +468,10 @@ int net_connect(struct socket *s) uint16_t i; i = s - sockets; i <<= 8; - wiz_writel(Sn_DIRP0 + i, s->s_addr[SADDR_DST].addr); - wiz_writew(Sn_DPORT0 + i, s->s_addr[SADDR_DST].port); - wiz_writeb(Sn_NR + i, CONNECT); + /* Already net endian */ + wiz_bwrite(Sn_DIPR0 + i, &s->s_addr[SADDR_DST].addr, 4); + wiz_bwrite(Sn_DPORT0 + i, &s->s_addr[SADDR_DST].port, 2); + wiz_writeb(Sn_CR + i, CONNECT); s->s_state = SS_CONNECTING; } else { /* UDP/RAW - note have to do our own filtering for 'connect' */ @@ -337,25 +484,30 @@ int net_connect(struct socket *s) void net_close(struct socket *s) { uint16_t i = s - sockets; + uint16_t off = i << 8; - if (s->s_type == SOCKTYPE_TCP) { - wiz_writeb(Sn_CR + (i << 8), DISCON); + if (s->s_type == SOCKTYPE_TCP && s->s_state != SS_CLOSED) { + wiz_writeb(Sn_CR + off, DISCON); s->s_state = SS_CLOSING; } else { - wiz_clear_int(i); - wiz_writeb(Sn_CR + (i << 8), CLOSE); - s->s_state = SS_UNINIT; + irqmask &= ~(1 << i); + wiz_writeb(IMR, irqmask); + wiz_writeb(Sn_CR + off, CLOSE); + sock_closed(s); } } arg_t net_read(struct socket *s, uint8_t flag) { - uint16_t n; + uint16_t n = 0xFFFF; uint16_t r; uint16_t i = s - sockets; + uint8_t st; i <<= 8; + s->s_iflag &= ~SI_DATA; + /* FIXME: IRQ protection */ /* Wait for data - push int core code ? */ while ((s->s_iflag & SI_DATA) == 0) { @@ -367,14 +519,19 @@ arg_t net_read(struct socket *s, uint8_t flag) /* Check for an EOF (covers post close cases too) */ if (s->s_iflag & SI_EOF) return 0; - /* Needs irq protection */ - if (psleep_flags(&s->iflag, flag)) - return -1; /* Keep waiting until we get the right state */ /* Bytes available */ - n = wiz_readw_n(Sn_RX_RSR + i); - if (n) + n = wiz_readw(Sn_RX_RSR + i); + if (n) { s->s_iflag |= SI_DATA; + break; + } + st = wiz_readb(Sn_SR); + if (st >= SOCK_CLOSING && st <= SOCK_UDP) + return 0; + /* Need IRQ protection to avoid sleep race */ + if (psleep_flags(&s->s_iflag, flag)) + return -1; } switch (s->s_type) { case SOCKTYPE_RAW: @@ -394,38 +551,44 @@ arg_t net_read(struct socket *s, uint8_t flag) /* Now dequeue some bytes into udata.u_base */ wiz_dequeue_u(i, r, udata.u_base); /* For datagrams we always discard the entire frame */ - wiz_writew_n(Sn_TX_WR0 + i, wiz_readw_n(Sn_TX_WR0 + i) - + s->s_type == SOCKTYPE_TCP ? r : (n + 8)); + if (s->s_type == SOCKTYPE_UDP) + r = n + 8; + wiz_writew(Sn_RX_RD0 + i, wiz_readw(Sn_RX_RD0 + i) + r); /* FIXME: figure out if SI_DATA should be cleared */ - return r; + /* Now tell the device we ate the data */ + wiz_writeb(Sn_CR, RECV); } + return r; } arg_t net_write(struct socket * s, uint8_t flag) { uint16_t i = s - sockets; uint16_t room; - uint16_t n; - uint8_t a = s->s_flag & SFLAG_ATMP ? SADDR_TMP : SADDR_SADDR_DST; + uint16_t n = 0;; + uint8_t a = s->s_flag & SFLAG_ATMP ? SADDR_TMP : SADDR_DST; + + /* FIXME: blocking ?? */ + used(flag); i <<= 8; - room = wiz_readw_n(Sn_TX_FSR + i); + room = wiz_readw(Sn_TX_FSR + i); - switch (s->s_socktype) { + switch (s->s_type) { case SOCKTYPE_UDP: if (udata.u_count > 1472) { udata.u_error = EMSGSIZE; return -1; } - case SOCKTYPE_IPRAW: + case SOCKTYPE_RAW: if (udata.u_count > 1500) { udata.u_error = EMSGSIZE; return -1; } if (room < udata.u_count) return -2; - wiz_writel(Sn_DIRPO + i, s->s_addr[a].addr); + wiz_writel(Sn_DIPR0 + i, s->s_addr[a].addr); wiz_writel(Sn_DPORT0 + i, s->s_addr[a].port); /* Fall through */ case SOCKTYPE_TCP: @@ -433,14 +596,24 @@ arg_t net_write(struct socket * s, uint8_t flag) return -2; n = min(room, udata.u_count); wiz_queue_u(i, n, udata.u_base); - wiz_writew_n(Sn_TX_WR0 + i, - wiz_readw_n(Sn_TX_WR0 + i) + n); + wiz_writew(Sn_TX_WR0 + i, + wiz_readw(Sn_TX_WR0 + i) + n); + wiz_writeb(Sn_CR, SEND); break; } - udata.u_count = n; return n; } +arg_t net_shutdown(struct socket *s, uint8_t flag) +{ + s->s_iflag |= flag; + if (s->s_iflag & SI_SHUTW) + wiz_writeb(Sn_CR, DISCON); + /* Really we need to look for SHUTR and received data and CLOSE if + so - FIXME */ + return 0; +} + /* Everything below this line is still pure sketching of ideas as we don't really have a configuration interface designed yet ! */ @@ -449,7 +622,7 @@ struct netdevice net_dev = { "eth0", /* Good a name as any */ 0, /* No special flags */ }; - +#if 0 /* Only some of these hit this code, most are handled by the core */ arg_t net_ioctl(uint8_t op, void *p) { @@ -472,18 +645,36 @@ arg_t net_ioctl(uint8_t op, void *p) wiz_bwrite(SHAR0, p, 6); break; case OP_GPHY: - return (wiz_bread(PSTATUS) & 0x20) ? LINK_UP : LINK_DOWN; + return (wiz_readb(PSTATUS) & 0x20) ? LINK_UP : LINK_DOWN; default: return -EINVAL; } return 0; } +#endif +static uint32_t ipa = 0x00000000; /* Tmp hack */ +static uint8_t fakeaddr[6] = { 0xC0, 0xFF, 0xEE, 0xC0, 0xFF, 0xEE }; +static uint32_t iga = 0x020000C0; +static uint32_t igm = 0x00FFFFFF; void netdev_init(void) { uint16_t i; - for (i = 0; i < 8; i++) { - wiz_writeb(Sn_RXMEM_SIZE + (i << 8), 0x2000); - wiz_writeb(Sn_TXMEM_SIZE + (i << 8), 0x2000); + /* We run all the time in indirect, autoinc */ + mr = MR_AUTOINC|MR_INDIRECT; + wiz_writeb(IMR, 0); +// wiz_writeb(RTR, ); +// wiz_writeb(RCR, ); + /* Set GAR, SHAR, SUBR, SIPR to defaults ? */ + wiz_bwrite(SIPR0, &ipa, 4); + wiz_bwrite(GAR0, &iga, 4); + wiz_bwrite(SUBR0, &igm, 4); + wiz_bwrite(SHAR0, fakeaddr, 6); + wiz_writeb(RMSR, 0x55); /* 2k a socket */ + wiz_writeb(TMSR, 0x55); /* 2k a socket */ + for (i = 0; i < 4 * 256; i += 256) { + /* Do we need to set anything here */ } } + +#endif diff --git a/Kernel/dev/net/net_wiznet.h b/Kernel/dev/net/net_wiznet.h new file mode 100644 index 00000000..cc3362b4 --- /dev/null +++ b/Kernel/dev/net/net_wiznet.h @@ -0,0 +1,3 @@ + +extern void wiz_event(void); +extern void wiz_poll(void); -- 2.34.1