From: Leandro Lucarella Date: Tue, 6 Dec 2005 07:58:21 +0000 (+0000) Subject: Primer intento de integración y creación del proyecto. X-Git-Tag: 0.1-recibe-matriz-raw-por-udp~28 X-Git-Url: https://git.llucax.com/z.facultad/66.09/etherled.git/commitdiff_plain/cbf2122a2f610a2b92f1dfcb0f554b4bf7c4757e?hp=7bcd0c7f950ee940e78515a697ee4b18990dc9b8 Primer intento de integración y creación del proyecto. El programa es medio bobo, sólo recibe frames, los parsea (ethernet, ip, udp) y muestra el payload de UDP. A pesar de esto no funciona, falta un poco de debugging, pero como está basando en 2 grandes fragmentos que funcionaban por separado, debería ser fácil ponerlo a andar. --- diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..9b5aa8b --- /dev/null +++ b/src/Makefile @@ -0,0 +1,35 @@ + +CC=sdcc + +LD=sdcc + +all: el.hex + +reg51.h: reg51sdcc.h + +netdev.h: types.h + +dp8390.h: types.h reg51.h netdev.h + +dp8390.rel: dp8390.c dp8390.h + $(CC) $(CFLAGS) -c dp8390.c + +eth.rel: eth.c eth.h netdev.h types.h + $(CC) $(CFLAGS) -c eth.c + +ip.rel: ip.c ip.h netdev.h types.h + $(CC) $(CFLAGS) -c ip.c + +udp.rel: udp.c udp.h ip.h netdev.h types.h + $(CC) $(CFLAGS) -c udp.c + +main.rel: main.c netdev.h eth.h ip.h udp.h + $(CC) $(CFLAGS) -c main.c + +el.hex: main.rel dp8390.rel eth.rel ip.rel udp.rel + $(LD) $(LDFLAGS) -o el.hex main.rel dp8390.rel eth.rel ip.rel udp.rel + +clean: + @rm -vf el.hex *.rel *.asm *.lst *.map *.lnk *.mem *.sym + +.PHONY: clean diff --git a/src/c/eth.c b/src/c/eth.c deleted file mode 100644 index 5c8686f..0000000 --- a/src/c/eth.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "eth.h" - -/** tipos de paquetes transportados soportados */ -enum { IP = 0x0800, ARP = 0x0806 }; - -byte mac_addr_local[MAC_ADDR_SIZE]; - -byte mac_addr_remote[MAC_ADDR_SIZE]; - -bool eth_frame_arp; - -bool eth_read_frame_header() -{ - /* variable para iterar */ - byte i; - /* vemos si es para nosotros */ - for (i = 0; i < MAC_ADDR_SIZE; ++i) - if (mac_addr_local[i] != net_getb()) - return false; /* no es para nosotros (drop) */ - /* obtenemos MAC de origen */ - for (i = 0; i < MAC_ADDR_SIZE; ++i) - mac_addr_remote[i] = net_getb(); - /* obtenemos tipo de protocolo transportado por el frame, (sólo - * aceptamos IP y ARP) */ - switch (net_getw()) - { - case IP: - eth_frame_arp = false; - break; - case ARP: - eth_frame_arp = true; - break; - default: - return false; /* drop */ - } - return true; -} - -void eth_write_frame_header() -{ - /* variable para iterar */ - byte i; - /* mandamos como MAC de destino la remota */ - for (i = 0; i < ETH_MAC_SIZE; ++i) - net_putb(mac_addr_remote[i]); - /* y como fuente la nuestra */ - for (i = 0; i < ETH_MAC_SIZE; ++i) - net_putb(mac_addr_local[i]); - /* escribimos el tipo de paquete que transporta el frame */ - net_putw(eth_frame_arp ? TYPE_ARP : TYPE_IP); -} - diff --git a/src/c/ip.c b/src/c/ip.c deleted file mode 100644 index 3f07cb2..0000000 --- a/src/c/ip.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "ip.h" - -/** protocolos soportados */ -enum { ICMP = 0x01, UDP = 0x11 }; - -byte ip_addr_local[IP_ADDR_SIZE]; - -byte ip_addr_remote[IP_ADDR_SIZE]; - -byte ip_packet_len; - -bool ip_proto_icmp; - -/* para calcular checksum */ -uint16 checksum; - -/* agrega un word al checksum calculado */ -static void sum(uint16 w) -{ - checksum += w; - if (checksum < w) /* corrección de carry (hubo OV) */ - ++checksum; -} - -bool ip_read_packet_header() -{ - /* variables utilitarias (iterar y/o buffer) */ - byte h, l; - /* reseteamos checksum */ - checksum = 0; - /* versión y tamaño de cabecera vienen en el 1er byte */ - h = net_getb(); - /* sólo soportamos versión 4 */ - if ((h >> 4) != 4) - return false; /* drop */ - /* tamaño de cabecera */ - if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */ - return false; /* drop */ - /* ignoramos el TOS y vamos calculando checksum */ - sum(WORD(h, net_getb())); - /* obtenemos tamaño del paquete */ - if (h = net_getb()) /* tiene más de 255 bytes (no lo soportamos) */ - return false; /* drop */ - ip_packet_len = net_getb(); /* hasta 255 bytes tienen los nuestros */ - /* vamos calculando checksum */ - sum(WORD(h, ip_packet_len)); - /* ignoramos identificación (2 bytes) y vamos calculando checksum */ - sum(net_getw()); - /* si tiene prendido el bit de MF (More Fragments, bit 5 del byte, bit 2 - * de los flags de la cabecera) o si tiene un offset de fragmento (bits - * del 4 al 0 y todos los bits del byte siguiente), dropeamos (no - * soportamos fragmentación) */ - h = net_getb(); - l = net_getb(); - if ((h & 0x3F) || l) - return false; /* drop */ - /* seguimos calculando checksum */ - sum(WORD(h, l)); - /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta - * acá el pobre =) */ - h = net_getb(); - /* protocolo (sólo soportamos UDP e ICMP) */ - l = net_getb(); - switch (l) - { - case ICMP: - ip_proto_icmp = true; - break; - case UDP: - ip_proto_icmp = false; - break; - default: - return false; /* drop */ - } - /* sigo calculando checksum */ - sum(WORD(h, l)); - /* obtenemos checksum y seguimos el cálculo */ - sum(net_getw()); - /* obtenemos IP de origen (mientras seguimos calculando el checksum) */ - for (l = 0; l < IP_ADDR_SIZE; ++l) - { - ip_addr_remote[l] = net_getb(); - if (l % 2) - sum(WORD(h, ip_addr_remote[l])); - else - h = ip_addr_remote[l]; - } - /* vemos si el paquete es para nosotros (ningún soportar broadcast =) - * (mientras seguimos calculando el checksum) */ - for (l = 0; l < IP_ADDR_SIZE; ++l) - { - if (ip_addr_local[l] != net_getb()) - return false; /* drop (no es para nosotros) */ - if (l % 2) - sum(WORD(h, ip_addr_local[l])); - else - h = ip_addr_local[l]; - } - /* verificamos checksum */ - if (~checksum) - return false; /* checksum malo, drop */ - return true; -} - -void ip_write_packet_header() -{ - /* variables utilitarias (iterar y/o buffer) */ - byte h, l; - /* identificador del paquete IP (incrementa con cada paquete) */ - static uint16 id; - /* reseteamos checksum */ - checksum = 0; - /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */ - net_putb(h = 0x45); - /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */ - l = ip_proto_icmp ? 0xc0 : 0x00; - net_putb(l); - sum(WORD(h, l)); /* actualizamos checksum */ - /* escribimos tamaño del paquete */ - net_putb(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */ - net_putb(ip_packet_len); - sum(WORD(h, ip_packet_len)); /* actualizamos checksum */ - /* identificación (sirve para reensamblar paquetes) */ - net_putw(id); - sum(id); /* actualizamos checksum */ - /* pedimos que no se fragmente */ - net_putb(h = 0x40); /* Don't Fragment (DF) = 1 */ - net_putb(l = 0x00); /* offset de fragmento = 0 */ - sum(WORD(h, l)); /* actualizamos checksum */ - /* TTL de 64 saltos porque está de moda */ - net_putb(h = 0x40); - /* protocolo (sólo soportamos UDP e ICMP) */ - l = ip_proto_icmp ? ICMP : UDP; - net_putb(l); - sum(WORD(h, l)); /* actualizamos checksum */ - /* checksum: antes de poder escribir el checksum hay que terminar de - * calcularlo según las direcciones IP de origen y destino, así que eso - * hacemos */ - for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */ - if (l % 2) - sum(WORD(h, ip_addr_local[l])); - else - h = ip_addr_local[l]; - for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */ - if (l % 2) - sum(WORD(h, ip_addr_remote[l])); - else - h = ip_addr_remote[l]; - /* ahora sí grabamos el checksum */ - net_putw(~checksum); - /* ahora sí, continuamos poniendo las direcciones */ - /* ponemos como dirección IP de origen la nuestra */ - for (i = 0; i < IP_ADDR_SIZE; ++i) - net_putb(ip_addr_local[i]); - /* IP de destino, la remota */ - for (i = 0; i < IP_ADDR_SIZE; ++i) - net_putb(ip_addr_remote[i]); -} - diff --git a/src/c/net.c b/src/c/net.c deleted file mode 100644 index 599e26d..0000000 --- a/src/c/net.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "net.h" - -uint16 net_getw() -{ - byte b = net_getb() << 8; - return b + net_getb(); -} - -void net_putw(uint16 w) -{ - net_putb(HIGH(w)); - net_putb(LOW(w)); -} - diff --git a/src/c/net.h b/src/c/net.h deleted file mode 100644 index 678513a..0000000 --- a/src/c/net.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _NET_H_ -#define _NET_H_ - -#include "types.h" - -/** lee un byte de la placa de red */ -byte net_getb(); - -/** escribe un byte a la placa de red */ -void net_putb(byte); - -/** lee un word de la placa de red */ -uint16 net_getw(); - -/** escribe un word a la placa de red */ -void net_putw(uint16); - -#endif /* _NET_H_ */ diff --git a/src/c/udp.c b/src/c/udp.c deleted file mode 100644 index c6fbfcc..0000000 --- a/src/c/udp.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "ip.h" -#include "udp.h" - -uint16 udp_port_local; - -uint16 udp_port_remote; - -byte udp_dgram_len; - -/* para calcular checksum */ -uint16 checksum; - -/* agrega un word al checksum calculado */ -void udp_checksum_sum(uint16 w) -{ - checksum += w; - if (checksum < w) /* corrección de carry (hubo OV) */ - ++checksum; -} - -/* indica si el checksum calculado está ok */ -bool udp_checksum_ok() -{ - return ~checksum; -} - -bool udp_read_dgram_header() -{ - /* reseteamos checksum */ - checksum = 0; - /* el UDP tiene un checksum que incluye parte de la cabecera IP */ - /* ip de origen */ - udp_checksum_sum(WORD(ip_addr_remote[0], ip_addr_remote[1])); - udp_checksum_sum(WORD(ip_addr_remote[2], ip_addr_remote[3])); - /* ip de destino */ - udp_checksum_sum(WORD(ip_addr_local[0], ip_addr_local[1])); - udp_checksum_sum(WORD(ip_addr_local[2], ip_addr_local[3])); - /* protocolo expresado en 16 bits (0x11 es UDP) */ - udp_checksum_sum(0x11); - /* tamaño del paquete UDP (sin las cabeceras que son 20 bytes) */ - udp_checksum_sum(ip_packet_len - 20); - /* de ahora en más todos los datos del checksum corresponden a UDP */ - /* puerto origen (remoto) */ - udp_port_remote = net_getw(); - /* agregamos puerto de origen al checksum */ - udp_checksum_sum(udp_port_remote); - /* sólo aceptamos datagramas a nuestro puerto */ - if (net_getw() != udp_port_local) - return false; /* drop */ - /* agregamos puerto de destino al checksum */ - udp_checksum_sum(udp_port_local); - /* tamaño del datagrama */ - if (net_getb()) /* no soportamos más de 255 bytes */ - return false; /* drop */ - udp_dgram_len = net_getb(); - /* agregamos tamaño al checksum */ - udp_checksum_sum(udp_dgram_len); - /* agregamos checksum al checksum */ - udp_checksum_sum(net_getw()); - /* falta agregar el cuerpo del mensaje para verificar la suma - * esto debe hacerlo el protocolo que sigue para poder seguir obteniendo - * los datos de la placa de red byte a byte */ - return true; -} - -void udp_write_dgram_header() -{ - /* puerto origen */ - net_putw(udp_port_local); - /* puerto destino */ - net_putw(udp_port_remote); - /* tamaño del datagrama */ - net_putb(0x00); /* parte alta en 0 porque no soportamos más de 255 */ - net_putb(udp_dgram_len); - /* indicamos que no se usa checksum */ - net_putw(0x0000); - return true; -} - diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..71f1727 --- /dev/null +++ b/src/config.h @@ -0,0 +1,10 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#define NET_RECV_CALLBACK eth_recv + +#define + +#endif // _CONFIG_H_ diff --git a/src/dp8390.c b/src/dp8390.c new file mode 100644 index 0000000..6392307 --- /dev/null +++ b/src/dp8390.c @@ -0,0 +1,477 @@ +// vim: set et sw=4 sts=4 : + +#include "eth.h" +#include "dp8390.h" + +#ifdef DEBUG +void sleep(unsigned char); +#ifdef SDCC +static xdata at 0x0080 byte leds0; +static xdata at 0x00c0 byte leds1; +#else +static byte xdata leds0 _at_ 0x0080; +static byte xdata leds1 _at_ 0x00c0; +#endif +#endif + +/// Datos persistentes del módulo +static union +{ + byte send_len; ///> Tamaño del frame que será enviado + byte next_pkt; ///> Próximo frame a obtener +} +persistent; + +/// Cambia de página sin modificar los demás bits del CR +#define SELECT_REG_PAGE(page) \ + do \ + { \ + write_reg(CR, read_reg(CR) & ~(PS1 | PS0)); \ + write_reg(CR, read_reg(CR) | (page << 6)); \ + } \ + while (0) + +/// Aborta (o completa) el DMA limpiando el ISR +#define ABORT_DMA(flags) \ + do \ + { \ + write_reg(CR, flags); \ + write_reg(ISR, RDC); \ + } \ + while (0) + + +static void write_reg(unsigned char reg, unsigned char wr_data) +{ + // Select register address. + ADDR_PORT &= ~ADDR_PORT_MASK; + ADDR_PORT |= reg; + + // Output register data to port. + DATA_PORT = wr_data; + + // Clock register data into RTL8019AS. + // IOR & IOW are both active low. + NICE = 0; + IOW = 0; + IOW = 1; + NICE = 1; + + // Set register data port as input again. + DATA_PORT = DATA_PORT_MASK; +} + + +static unsigned char read_reg(unsigned char reg) +{ + // Select register address. + ADDR_PORT &= ~ADDR_PORT_MASK; + ADDR_PORT |= reg; + + // Enable register data output from RTL8019AS. + NICE = 0; + IOR = 0; + + // Read register data from port. + reg = DATA_PORT; + + // Disable register data output from RTL8019AS. + IOR = 1; + NICE = 1; + + return reg; +} + +/** Resetea placa de red en caso de buffer overflow */ +static void reset() +{ + bit retransmit = read_reg(CR) & TXP; + + // If the receive buffer ring has overflowed we dump the whole + // thing and start over. There is no way of knowing whether the + // data it contains is uncorrupted, or will cause us grief. + + // Stop RTL8019AS and abort DMA operation. + write_reg(CR, STOP); + + // Wait for controller to halt after any current tx completes. + while(!(read_reg(ISR) & RST)) continue; + + // Reset remote byte count registers. + write_reg(RBCR0, 0x00); + write_reg(RBCR1, 0x00); + + // Check whether currently transmitting a packet. + if(retransmit) + { + // If neither a successful transmission nor a tx abort error + // has occured, then flag current tx packet for resend. + if(read_reg(ISR) & (PTX | TXE)) + { + retransmit = 0; + } + } + + // Set transmit configuration register to loopback internally. + write_reg(TCR, MODE1); + + // Restart the RTL8019AS. + write_reg(CR, START); + + // Re-initialise last receive buffer read pointer. + write_reg(BNRY, RX_PAGE_START); + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(1); + + // Re-initialise current packet receive buffer page pointer. + write_reg(CURR, RX_PAGE_START + 1); + + // Select RTL8019AS register page 0. + SELECT_REG_PAGE(0); + + // Clear rx buffer overflow & packet received interrupt flags. + write_reg(ISR, PRX | OVW); + + // Re-itialise transmit configuration reg for normal operation. + write_reg(TCR, MODE0); + + if(retransmit) + { + // Retransmit packet in RTL8019AS local tx buffer. + write_reg(CR, START | TXP); + } +} + + +/** Inicializa dispositivo de red + * @return true si se inicializó correctamente, false si no + */ +bool netdev_init() +{ + // Set IOR & IOW as they're active low. + IOR = 1; + IOW = 1; + NICE = 1; + + // Set register data port as input. + DATA_PORT = DATA_PORT_MASK; + + // Configure RTL8019AS ethernet controller. + + // Keil startup code takes 4ms to execute (18.432MHz, X1 mode). + // That leaves plenty of time for the RTL8019AS to read it's + // configuration in from the 9346 EEPROM before we get here. + + // Select RTL8019AS register page 0. + SELECT_REG_PAGE(0); + + // Check if RTL8019AS fully reset. + if(!(read_reg(ISR) & RST)) + { + return 0; + } + + // Stop RTL8019AS, select page 0 and abort DMA operation. + write_reg(CR, STOP); + + // Initialise data configuration register. + // FIFO threshold 8 bytes, no loopback, don't use auto send packet. + write_reg(DCR, FT1 | LS); + + // Reset remote byte count registers. + write_reg(RBCR0, 0u); + write_reg(RBCR1, 0u); + + // Receive configuration register to monitor mode. + write_reg(RCR, MON); + + // Initialise transmit configuration register to loopback internally. + write_reg(TCR, MODE1); + + // Clear interrupt status register bits by writing 1 to each. + write_reg(ISR, ALL); + + // Mask all interrupts in mask register. + write_reg(IMR, NONE); + + // Obtengo MAC de la placa + write_reg(RBCR0, 12u); // Vamos a leer 12 bytes (2 x 6) + write_reg(RBCR1, 0u); + write_reg(RSAR0, 0u); // En la dirección 0x0000 + write_reg(RSAR1, 0u); + write_reg(CR, READ); // Comienza lectura + eth_addr_local[0] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + eth_addr_local[1] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + eth_addr_local[2] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + eth_addr_local[3] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + eth_addr_local[4] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + eth_addr_local[5] = read_reg(RDMA); + read_reg(RDMA); // Ignoramos porque viene como un word + + // Abort/ complete DMA operation. + ABORT_DMA(STOP); + + // Set transmit page start. + write_reg(TPSR, TX_PAGE_START); + + // Set receive buffer page start. + write_reg(PSTART, RX_PAGE_START); + + // Initialise last receive buffer read pointer. + write_reg(BNRY, RX_PAGE_START); + + // Set receive buffer page stop. + write_reg(PSTOP, RX_PAGE_STOP); + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(1); + + // Initialise current packet receive buffer page pointer + write_reg(CURR, RX_PAGE_START + 1); + + // Set physical address + write_reg(PAR0, eth_addr_local[0]); + write_reg(PAR1, eth_addr_local[1]); + write_reg(PAR2, eth_addr_local[2]); + write_reg(PAR3, eth_addr_local[3]); + write_reg(PAR4, eth_addr_local[4]); + write_reg(PAR5, eth_addr_local[5]); + + // Restart RTL8019AS. + write_reg(CR, START); + + // Initialise transmit configuration register for normal operation. + write_reg(TCR, MODE0); + + // Receive configuration register to accept broadcast packets. + write_reg(RCR, AB); + + return 1; +} + + +/** Comienza el envío de un nuevo frame + * @param len Tamaño del frame a enviar + */ +void netdev_send_start() +{ + persistent.send_len = 0; + // Wait until pending transmit operation completes. + while(read_reg(CR) & TXP) continue; + + // Set remote DMA start address registers to indicate where to load packet. + write_reg(RSAR0, 0u); + write_reg(RSAR1, TX_PAGE_START); + + // Set remote DMA byte count registers to indicate length of packet load. + write_reg(RBCR0, MAX_PACKET_LEN); // Tamaño máximo en principio + write_reg(RBCR1, 0u); + + // Initiate DMA transfer of uip_buf & uip_appdata buffers to RTL8019AS. + write_reg(CR, WRITE | STA); +} + +/** Escribe un byte al buffer de la placa de red para ser enviado + * @precond netdev_send_start() debe haber sido ejecutada + * @param b Byte a enviar + */ +void netdev_send_byte(byte b) +{ + persistent.send_len++; + write_reg(RDMA, b); +} + +/** Escribe un word al buffer de la placa de red para ser enviado + * @precond netdev_send_start() debe haber sido ejecutada + * @param w Word a enviar + */ +void netdev_send_word(uint16 w) +{ + persistent.send_len += 2; + write_reg(RDMA, HIGH(w)); + write_reg(RDMA, LOW(w)); +} + +/** Finaliza el envío del frame + * @precond netdev_send_start() debe haber sido ejecutada + */ +void netdev_send_end() +{ + // Abort/ complete DMA operation. + ABORT_DMA(START); + + // Set transmit page start to indicate packet start. + write_reg(TPSR, TX_PAGE_START); + + // Ethernet packets must be > 60 bytes, otherwise are rejected as runts. + if (persistent.send_len < MIN_PACKET_LEN) + { + persistent.send_len = MIN_PACKET_LEN; + } + + // Set transmit byte count registers to indicate packet length. + write_reg(TBCR0, LOW(persistent.send_len)); + write_reg(TBCR1, 0u); + + // Issue command for RTL8019AS to transmit packet from it's local buffer. + write_reg(CR, START | TXP); +} + +/** Comienza la lectura de un nuevo frame + * @return Cantidad de bytes del frame leído + */ +byte netdev_recv_start() +{ + // Check if the rx buffer has overflowed. + if (read_reg(ISR) & OVW) + { + byte current; + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(1); + + // Retrieve current receive buffer page + current = read_reg(CURR); + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(0); + + if (read_reg(BNRY) == current) + { +#ifdef DEBUG + leds1 = ~0x01; + leds2 = ~read_reg(ISR); + sleep(5); + leds1 = ~0x02; + leds2 = ~read_reg(BNRY); + sleep(5); + leds1 = ~0x04; + leds2 = ~current; + sleep(5); +#endif + reset(); + } + return 0u; + } + // Check if there is a packet in the rx buffer. + else if (read_reg(ISR) & PRX) + { + struct buf_hdr_t + { + byte status; // Estado del frame recibido + byte next; // Offset del próximo frame + uint16 len; // Tamaño del frame + } + buf_hdr; + byte current; + byte bnry; + + // Retrieve packet header. (status, next_ptr, length_l, length_h) + + // Set remote DMA start address registers to packet header. + bnry = read_reg(BNRY) + 1; + if (bnry >= RX_PAGE_STOP) + bnry = RX_PAGE_START; + write_reg(RSAR0, 0u); + write_reg(RSAR1, bnry); + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(1); + + // Retrieve current receive buffer page + current = read_reg(CURR); + + // Select RTL8019AS register page 1. + SELECT_REG_PAGE(0); + + // Check if last packet has been removed from rx buffer. + if(bnry == current) + { + // Clear packet received interrupt flag. + write_reg(ISR, PRX | RXE); + return 0u; + } + + // Set remote DMA byte count registers to packet header length. + write_reg(RBCR0, sizeof(struct buf_hdr_t)); + write_reg(RBCR1, 0u); + + // Clear remote DMA complete interrupt status register bit. + write_reg(ISR, RDC); + + // Initiate DMA transfer of packet header. + write_reg(CR, READ); + + // Packet status. + buf_hdr.status = read_reg(RDMA); + + // Save next packet pointer. + buf_hdr.next = persistent.next_pkt = read_reg(RDMA); + + // Retrieve packet data length and subtract CRC bytes. + buf_hdr.len = read_reg(RDMA) - sizeof(struct buf_hdr_t); + + // Si es muy grande, muy chico o hubo error, lo descartamos + if ((buf_hdr.len < MIN_PACKET_LEN) || (buf_hdr.len > MAX_PACKET_LEN) + || ((buf_hdr.status & 0x0F) != RXSOK) + || read_reg(RDMA)) // Parte alta del tamaño + { + ABORT_DMA(START); // Termina DMA + write_reg(BNRY, buf_hdr.next - 1); // Pasa al próximo frame + return 0; + } + + // Abort/ complete DMA operation. + ABORT_DMA(START); + + // Set remote DMA start address registers to packet data. + write_reg(RSAR0, sizeof(struct buf_hdr_t)); + write_reg(RSAR1, bnry); + + // Set remote DMA byte count registers to packet data length. + write_reg(RBCR0, buf_hdr.len); + write_reg(RBCR1, 0u); + + // Initiate DMA transfer of packet data. + write_reg(CR, READ); + + return buf_hdr.len; + } + return 0; +} + +/** Lee un byte del buffer de la placa de red + * @precond netdev_recv_start() debe haber sido ejecutada + */ +byte netdev_recv_byte() +{ + return read_reg(RDMA); +} + +/** Lee un word del buffer de la placa de red + * @precond netdev_recv_start() debe haber sido ejecutada + */ +uint16 netdev_recv_word() +{ + uint16 w = netdev_recv_byte() << 8; + return w + netdev_recv_byte(); +} + +/** Finaliza la lectura del frame + * @precond netdev_recv_start() debe haber sido ejecutada + */ +void netdev_recv_end() +{ + // Abort/ complete DMA operation. + ABORT_DMA(START); + + // Advance boundary pointer to next packet start. + write_reg(BNRY, persistent.next_pkt - 1); +} + diff --git a/src/dp8390.h b/src/dp8390.h new file mode 100644 index 0000000..0d2fa39 --- /dev/null +++ b/src/dp8390.h @@ -0,0 +1,235 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _DP8390_H_ +#define _DP8390_H_ + +#include "reg51.h" +#include "types.h" +#include "netdev.h" + +// Configuración de puertos para comunicarse con la placa de red +#define DATA_PORT P2 // Adjust this to suit hardware. +#define DATA_PORT_MASK 0xFF // Máscara para leer del puerto +#define ADDR_PORT P1 // Adjust this to suit hardware. +#define ADDR_PORT_MASK 0x1F // Máscara de direcciones (para no cambiar + // bits que no se usan) +#ifdef SDCC +sbit at 0xB2 IOW; // ISA slot pin B13, RTL8019AS pin 30, active low +sbit at 0xB5 IOR; // ISA slot pin B14, RTL8019AS pin 29, active low +sbit at 0xB2 NICE; // A7, usado para activar placa de red +#else +#define CTRL_PORT P3 // Adjust this to suit hardware. +sbit IOW = CTRL_PORT^4; // ISA slot pin B13, RTL8019AS pin 30, active low +sbit IOR = CTRL_PORT^5; // ISA slot pin B14, RTL8019AS pin 29, active low +sbit NICE = CTRL_PORT^2; // A7, usado para activar placa de red +#endif + +// Configuración de paǵinas de buffers +#define TX_PAGE_START 0x40 // 0x4000 Tx buffer: 6 * 256 = 1536 bytes +#define RX_PAGE_START 0x46 // 0x4600 Rx buffer: 26 * 256 = 6656 bytes +#define RX_PAGE_STOP 0x60 // 0x6000 + +// Límites de tamaño de paquete +#define MIN_PACKET_LEN 60u // Mínimo permitido por 802.3 +#define MAX_PACKET_LEN 128u // Mínimo permitido por nuestra escasa memoria =) + +// Register base address +#define REG_BASE 0x0000 // Hardwired to 0x0300 + +// Registers common to all pages. +#define CR REG_BASE + 0x00 // Control register + // Control register bits + #define PS1 0x80 // Page select bit 1 + #define PS0 0x40 // Page select bit 0 + #define RD2 0x20 // Remote DMA control bit 2 + #define RD1 0x10 // Remote DMA control bit 1 + #define RD0 0x08 // Remote DMA control bit 0 + #define TXP 0x04 // Transmit packet bit + #define STA 0x02 // Start bit (a flag only) + #define STP 0x01 // Stop bit transceiver ctrl + // Shortcuts + #define PAGE0 0x00 // Page 0 + #define PAGE1 0x40 // Page 1 + #define PAGE2 0x80 // Page 2 + #define PAGE3 0xC0 // Page 3 (Reserved!) + #define ABORT 0x20 // Abort/Complete DMA + #define READ 0x0A // Remote Read (RD0 | STA) + #define WRITE 0x12 // Remote Write (RD1 | STA) + #define SENDPKT 0x1A // Send Packet (RD0 | RD1 | STA) + #define STOP 0x21 // STP | ABORT + #define START 0x22 // STA | ABORT + +#define RDMA 0x10 // Remote DMA port +#define RESET 0x18 // Reset port + +// Page 0 read/write registers. +#define BNRY REG_BASE + 0x03 // Boundary register +#define ISR REG_BASE + 0x07 // Interrupt status register + // Interrupt status register bits + #define RST 0x80 // Reset state indicator bit + #define RDC 0x40 // Remote DMA complete bit + #define CNT 0x20 // Network tally counter MSB set + #define OVW 0x10 // Receive buffer exhausted + #define TXE 0x08 // Transmit abort error bit + #define RXE 0x04 // Receive error report bit + #define PTX 0x02 // Successful packet transmit + #define PRX 0x01 // Successful packet receive + // Shortcuts + #define ALL 0xFF // Todos los bits + #define NONE 0x00 // Ninguno + +// Page 0 read only registers. +#define CLDA0 REG_BASE + 0x01 +#define CLDA1 REG_BASE + 0x02 +#define TSR REG_BASE + 0x04 +#define NCR REG_BASE + 0x05 +#define FIFO REG_BASE + 0x06 +#define CRDA0 REG_BASE + 0x08 +#define CRDA1 REG_BASE + 0x09 +#define CONFIGA REG_BASE + 0x0A +#define CONFIGB REG_BASE + 0x0B +#define RSR REG_BASE + 0x0C +#define CNTR0 REG_BASE + 0x0D +#define CNTR1 REG_BASE + 0x0E +#define CNTR2 REG_BASE + 0x0F + +// Page 0 write only registers. +#define PSTART REG_BASE + 0x01 // Receive page start register +#define PSTOP REG_BASE + 0x02 // Receive page stop register +#define TPSR REG_BASE + 0x04 // Transmit page start register +#define TBCR0 REG_BASE + 0x05 // Transmit byte count register 0 +#define TBCR1 REG_BASE + 0x06 // Transmit byte count register 1 +#define RSAR0 REG_BASE + 0x08 // Remote start address register 0 +#define RSAR1 REG_BASE + 0x09 // Remote start address register 0 +#define RBCR0 REG_BASE + 0x0A // Remote byte count register 0 +#define RBCR1 REG_BASE + 0x0B // Remote byte count register 1 +#define RCR REG_BASE + 0x0C // Receive configuration register + // Receive configuration register bits (write in page 0, read in page 2) + #define MON 0x20 // Monitor mode select bit + #define PRO 0x10 // Promiscuous mode select bit + #define AM 0x08 // Multicast packet accept bit + #define AB 0x04 // Broadcast packet accept bit + #define AR 0x02 // Runt packet accept bit + #define SEP 0x01 // Error packet accept bit +#define TCR REG_BASE + 0x0D // Transmit configuration register + // Transmit configuration register bits + #define OFST 0x10 // Collision offset enable bit + #define ATD 0x08 // Auto transmit disable select bit + #define LB1 0x04 // Loopback mode select bit 1 + #define LB0 0x02 // Loopback mode select bit 0 + #define CRC 0x01 // CRC generation inhibit bit + // Shortcuts + #define MODE0 0x00 // Loopback mode 0 + #define MODE1 0x02 // Loopback mode 1 + #define MODE2 0x04 // Loopback mode 2 + #define MODE3 0x06 // Loopback mode 3 +#define DCR REG_BASE + 0x0E // Data configuration register + // Data configuration register bits (write in page 0, read in page 2) + #define FT1 0x40 // FIFO threshold select bit 1 + #define FT0 0x20 // FIFO threshold select bit 0 + #define ARM 0x10 // Auto-initialise remote + #define LS 0x08 // Loopback select bit + #define LAS 0x04 // Set to 0 (pwrup = 1) + #define BOS 0x02 // Byte order select bit + #define WTS 0x01 // Word transfer select bit +#define IMR REG_BASE + 0x0F // Interrupt mask register + // Interrupt mask register bits + // Each enable bit correspons with an interrupt flag in ISR + +// Page 1 read/write registers. +#define PAR0 REG_BASE + 0x01 // Physical address register 0 +#define PAR1 REG_BASE + 0x02 // Physical address register 1 +#define PAR2 REG_BASE + 0x03 // Physical address register 2 +#define PAR3 REG_BASE + 0x04 // Physical address register 3 +#define PAR4 REG_BASE + 0x05 // Physical address register 4 +#define PAR5 REG_BASE + 0x06 // Physical address register 5 +#define CURR REG_BASE + 0x07 // Current receive buffer page +#define MAR0 REG_BASE + 0x08 +#define MAR1 REG_BASE + 0x09 +#define MAR2 REG_BASE + 0x0A +#define MAR3 REG_BASE + 0x0B +#define MAR4 REG_BASE + 0x0C +#define MAR5 REG_BASE + 0x0D +#define MAR6 REG_BASE + 0x0E +#define MAR7 REG_BASE + 0x0F + +// Page 2 read only registers. +// Each previously defined in page 0 write only. +//#define PSTART REG_BASE + 0x01 +//#define PSTOP REG_BASE + 0x02 +//#define TPSR REG_BASE + 0x04 +//#define RCR REG_BASE + 0x0C +//#define TCR REG_BASE + 0x0D +//#define DCR REG_BASE + 0x0E +//#define IMR REG_BASE + 0x0F + +// Page 3 read/write registers. +#define _9346CR REG_BASE + 0x01 // 9346 EEPROM command register + // 9346 EEPROM command register bits + #define EEM1 0x80 // RTL8019AS operating mode bit 1 + #define EEM0 0x40 // RTL8019AS operating mode bit 0 + #define EECS 0x08 // 9346 EEPROM chip select bit + #define EESK 0x04 // 9346 EEPROM serial clock bit + #define EEDI 0x02 // 9346 EEPROM data input bit + #define EEDO 0x01 // 9346 EEPROM data output bit +#define BPAGE REG_BASE + 0x02 +#define CONFIG1 REG_BASE + 0x04 // RTL9019AS config register 1 + // RTL9019AS config register 1 bits + #define IRQEN 0x80 // IRQ enable bit (WR protected) + #define IRQS2 0x40 // IRQ line select bit 2 + #define IRQS1 0x20 // IRQ line select bit 1 + #define IRQS0 0x10 // IRQ line select bit 0 + #define IOS3 0x08 // I/O base address select bit 3 + #define IOS2 0x04 // I/O base address select bit 2 + #define IOS1 0x02 // I/O base address select bit 1 + #define IOS0 0x01 // I/O base address select bit 0 +#define CONFIG2 REG_BASE + 0x05 // + // RTL9019AS config register 2 bits + #define PL1 0x80 // Network medium type select bit 1 + #define PL0 0x40 // Network medium type select bit 0 + #define BSELB 0x20 // Boot ROM disable (WR protected) + #define BS4 0x10 // Boot ROM configuration bit 4 + #define BS3 0x08 // Boot ROM configuration bit 3 + #define BS2 0x04 // Boot ROM configuration bit 2 + #define BS1 0x02 // Boot ROM configuration bit 1 + #define BS0 0x01 // Boot ROM configuration bit 0 +#define CONFIG3 REG_BASE + 0x06 // RTL9019AS config register 3 + // RTL9019AS config register 3 bits + #define PNP 0x80 // Plug & play mode indicator bit + #define FUDUP 0x40 // Full duplex mode select bit + #define LEDS1 0x20 // LED output select bit 1 + #define LEDS0 0x10 // LED output select bit 0 + #define SLEEP 0x04 // Sleep mode select bit + #define PWRDN 0x02 // Power down mode select bit + #define ACTIVEB 0x01 // Inverse of bit 0, PNP active reg + +// Page 3 read only registers. +#define CONFIG0 REG_BASE + 0x03 // RTL9019AS config register 0 + // RTL9019AS config register 0 bits + #define VERID1 0x80 // RTL9019AS version ID bit 1 (R/W) + #define VERID0 0x40 // RTL9019AS version ID bit 0 (R/W) + #define AUI 0x20 // AUI input pin state bit + #define PNPJP 0x10 // PNP jumper pin state bit + #define JP 0x08 // JP input pin state bit + #define BNC 0x04 // Thinnet mode indication bit +#define CSNSAV REG_BASE + 0x08 +#define INTR REG_BASE + 0x0B +#define CONFIG4 REG_BASE + 0x0D + +// Page 3 write only registers. +#define TEST REG_BASE + 0x07 +#define HLTCLK REG_BASE + 0x09 +#define FMWP REG_BASE + 0x0C + + +// Bits del byte de status del frame recibido +#define RXSOK 0x01 /* Received a good packet */ +#define RXSCRC 0x02 /* CRC error */ +#define RXSFAE 0x04 /* frame alignment error */ +#define RXSFO 0x08 /* FIFO overrun */ +#define RXSMPA 0x10 /* missed pkt */ +#define RXSPHY 0x20 /* physical/multicast address */ +#define RXSDIS 0x40 /* receiver disable. set in monitor mode */ +#define RXSDEF 0x80 /* deferring */ + +#endif diff --git a/src/eth.c b/src/eth.c new file mode 100644 index 0000000..6b9e162 --- /dev/null +++ b/src/eth.c @@ -0,0 +1,54 @@ +// vim: set et sw=4 sts=4 : + +#include "netdev.h" +#include "eth.h" + +/** tipos de paquetes transportados soportados */ +enum { IP = 0x0800, ARP = 0x0806 }; + +byte eth_addr_local[ETH_ADDR_SIZE]; + +byte eth_addr_remote[ETH_ADDR_SIZE]; + +eth_proto_t eth_proto; + +bool eth_read_frame_header() +{ + /* variable para iterar */ + byte i; + /* descarto MAC de destino, acepto broadcasts */ + for (i = 0; i < ETH_ADDR_SIZE; ++i) + netdev_recv_byte(); + /* obtenemos MAC de origen */ + for (i = 0; i < ETH_ADDR_SIZE; ++i) + eth_addr_remote[i] = netdev_recv_byte(); + /* obtenemos tipo de protocolo transportado por el frame, (sólo + * aceptamos IP y ARP) */ + switch (netdev_recv_word()) + { + case IP: + eth_proto = ETH_IP; + break; + case ARP: + eth_proto = ETH_ARP; + break; + default: + return false; /* drop */ + } + return true; +} + +void eth_write_frame_header() +{ + /* variable para iterar */ + byte i; + /* mandamos como MAC de destino la remota */ + for (i = 0; i < ETH_ADDR_SIZE; ++i) + netdev_send_byte(eth_addr_remote[i]); + /* y como fuente la nuestra */ + for (i = 0; i < ETH_ADDR_SIZE; ++i) + netdev_send_byte(eth_addr_local[i]); + /* escribimos el tipo de paquete que transporta el frame */ + netdev_send_word((eth_proto == ETH_IP) ? IP : ARP); +} + diff --git a/src/c/eth.h b/src/eth.h similarity index 79% rename from src/c/eth.h rename to src/eth.h index 54b1f53..9a640de 100644 --- a/src/c/eth.h +++ b/src/eth.h @@ -1,8 +1,9 @@ +// vim: set et sw=4 sts=4 : + #ifndef _ETH_H_ #define _ETH_H_ #include "types.h" -#include "net.h" /** @file * Estructura de un frame ethernet. @@ -20,16 +21,19 @@ */ /** Tamaño de dirección MAC (en bytes) */ -#define MAC_ADDR_SIZE 6 +#define ETH_ADDR_SIZE 6 + +/** Tipos de frame ethernet */ +typedef enum { ETH_IP, ETH_ARP } eth_proto_t; /** Dirección MAC nuestra */ -extern byte mac_addr_local[ETH_MAC_SIZE]; +extern byte eth_addr_local[ETH_ADDR_SIZE]; /** Dirección MAC de destino */ -extern byte mac_addr_remote[ETH_MAC_SIZE]; +extern byte eth_addr_remote[ETH_ADDR_SIZE]; -/** Indica si el frame transporta ARP (si no transporta IP) */ -extern bool eth_frame_arp; /* FIXME: debería ser un bit manejado con ASM supongo */ +/** Indica que protocolo está transportando el frame */ +extern eth_proto_t eth_proto; /** * Lee la cabecera del frame ethernet. diff --git a/src/etherled.Uv2 b/src/etherled.Uv2 new file mode 100644 index 0000000..f9bea75 --- /dev/null +++ b/src/etherled.Uv2 @@ -0,0 +1,135 @@ +### uVision2 Project, (C) Keil Software +### Do not modify ! + +Target (etherled), 0x0000 // Tools: 'MCS-51' + +Group (net) +Group (common) + +File 1,5,<.\netdev.h> 0x0 +File 1,5,<.\dp8390.h> 0x0 +File 1,1,<.\dp8390.c> 0x0 +File 1,5,<.\eth.h> 0x0 +File 1,1,<.\eth.c> 0x0 +File 1,5,<.\ip.h> 0x0 +File 1,1,<.\ip.c> 0x0 +File 1,5,<.\udp.h> 0x0 +File 1,1,<.\udp.c> 0x0 +File 2,1,<.\main.c> 0x0 +File 2,5,<.\reg51.h> 0x0 +File 2,2,<.\startup.a51> 0x0 +File 2,5,<.\types.h> 0x0 + + +Options 1,0,0 // Target 'etherled' + Device (AT89S8252) + Vendor (Atmel) + Cpu (IRAM(0-0xFF) IROM(0-0x1FFF) CLOCK(24000000) MODA2) + FlashUt () + StupF ("LIB\STARTUP.A51" ("Standard 8051 Startup Code")) + FlashDR () + DevID () + Rgf (REG8252.H) + Mem () + C () + A () + RL () + OH () + DBC_IFX () + DBC_CMS () + DBC_AMS () + DBC_LMS () + UseEnv=0 + EnvBin () + EnvInc () + EnvLib () + EnvReg (ÿAtmel\) + OrgReg (ÿAtmel\) + TgStat=25 + OutDir (.\) + OutName (etherled) + GenApp=1 + GenLib=0 + GenHex=1 + Debug=1 + Browse=1 + LstDir (.\) + HexSel=0 + MG32K=0 + TGMORE=0 + RunUsr 0 0 <> + RunUsr 1 0 <> + BrunUsr 0 0 <> + BrunUsr 1 0 <> + SVCSID <> + MODEL5=0 + RTOS5=0 + ROMSZ5=2 + DHOLD5=0 + XHOLD5=0 + T51FL=208 + XT51FL=0 + CBANKS5=0 + XBANKS5=0 + RCB51 { 0,0,0,0,0,0,0,1,0 } + RXB51 { 0,0,0,0,0,0,0,0,0 } + OCM51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } + OCR51 { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } + IRO51 { 1,0,0,0,0,0,32,0,0 } + IRA51 { 0,0,0,0,0,0,1,0,0 } + XRA51 { 0,0,0,0,0,0,0,0,0 } + XRA512 { 0,0,0,0,0,0,0,0,0 } + IROM512 { 0,0,0,0,0,0,0,0,0 } + C51FL=21630224 + C51VA=0 + C51MSC () + C51DEF () + C51UDF () + INCC5 () + AX51FL=4 + AX51MSC () + AX51SET () + AX51RST () + INCA5 () + PropFld { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } + IncBld=1 + AlwaysBuild=0 + GenAsm=0 + AsmAsm=0 + PublicsOnly=0 + StopCode=3 + CustArgs () + LibMods () + BankNo=65535 + LX51FL=292 + LX51OVL () + LX51MSC () + LX51DWN () + LX51LFI () + LX51ASN () + LX51RES () + LX51CCL () + LX51UCL () + LX51CSC () + LX51UCS () + LX51COB () + LX51XDB () + LX51PDB () + LX51BIB () + LX51DAB () + LX51IDB () + LX51PRC () + LX51STK () + LX51COS () + LX51XDS () + LX51BIS () + LX51DAS () + LX51IDS () + OPTDL (S8051.DLL)()(DP51.DLL)(-p8252)(S8051.DLL)()(TP51.DLL)(-p8252) + OPTDBG 48125,0,()()()()()()()()()() ()()()() + FLASH1 { 0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,0,0,0,0 } + FLASH2 () + FLASH3 () + FLASH4 () +EndOpt + diff --git a/src/ip.c b/src/ip.c new file mode 100644 index 0000000..0b4b2e2 --- /dev/null +++ b/src/ip.c @@ -0,0 +1,163 @@ +// vim: set et sw=4 sts=4 : + +#include "netdev.h" +#include "ip.h" + +/** protocolos soportados */ +enum { ICMP = 0x01, UDP = 0x11 }; + +byte ip_addr_local[IP_ADDR_SIZE]; + +byte ip_addr_remote[IP_ADDR_SIZE]; + +byte ip_packet_len; + +ip_proto_t ip_proto; + +/* para calcular checksum */ +static uint16 checksum; + +/* agrega un word al checksum calculado */ +static void sum(uint16 w) +{ + checksum += w; + if (checksum < w) /* corrección de carry (hubo OV) */ + ++checksum; +} + +bool ip_read_packet_header() +{ + /* variables utilitarias (iterar y/o buffer) */ + byte h, l; + /* reseteamos checksum */ + checksum = 0; + /* versión y tamaño de cabecera vienen en el 1er byte */ + h = netdev_recv_byte(); + /* sólo soportamos versión 4 */ + if ((h >> 4) != 4) + return false; /* drop */ + /* tamaño de cabecera */ + if ((h & 0x0F) != 5) /* no aceptamos opciones raras =) */ + return false; /* drop */ + /* ignoramos el TOS y vamos calculando checksum */ + sum(WORD(h, netdev_recv_byte())); + /* obtenemos tamaño del paquete */ + if (h = netdev_recv_byte()) /* tiene más de 255 bytes (no lo soportamos) */ + return false; /* drop */ + ip_packet_len = netdev_recv_byte(); /* hasta 255 bytes tienen los nuestros */ + /* vamos calculando checksum */ + sum(WORD(h, ip_packet_len)); + /* ignoramos identificación (2 bytes) y vamos calculando checksum */ + sum(netdev_recv_word()); + /* si tiene prendido el bit de MF (More Fragments, bit 5 del byte, bit 2 + * de los flags de la cabecera) o si tiene un offset de fragmento (bits + * del 4 al 0 y todos los bits del byte siguiente), dropeamos (no + * soportamos fragmentación) */ + h = netdev_recv_byte(); + l = netdev_recv_byte(); + if ((h & 0x3F) || l) + return false; /* drop */ + /* seguimos calculando checksum */ + sum(WORD(h, l)); + /* no le damos bola al TTL (no le vamos a hacer lío si ya llegó hasta + * acá el pobre =) */ + h = netdev_recv_byte(); + /* protocolo (sólo soportamos UDP e ICMP) */ + l = netdev_recv_byte(); + switch (l) + { + case ICMP: + ip_proto = IP_ICMP; + break; + case UDP: + ip_proto = IP_UDP; + break; + default: + return false; /* drop */ + } + /* sigo calculando checksum */ + sum(WORD(h, l)); + /* obtenemos checksum y seguimos el cálculo */ + sum(netdev_recv_word()); + /* obtenemos IP de origen (mientras seguimos calculando el checksum) */ + for (l = 0; l < IP_ADDR_SIZE; ++l) + { + ip_addr_remote[l] = netdev_recv_byte(); + if (l % 2) + sum(WORD(h, ip_addr_remote[l])); + else + h = ip_addr_remote[l]; + } + /* vemos si el paquete es para nosotros (ningún soportar broadcast =) + * (mientras seguimos calculando el checksum) */ + // TODO si soportamos DHCP hay que aceptar broadcasts! + for (l = 0; l < IP_ADDR_SIZE; ++l) + { + if (ip_addr_local[l] != netdev_recv_byte()) + return false; /* drop (no es para nosotros) */ + if (l % 2) + sum(WORD(h, ip_addr_local[l])); + else + h = ip_addr_local[l]; + } + /* verificamos checksum */ + if ((uint16)~checksum) + return false; /* checksum malo, drop */ + return true; +} + +void ip_write_packet_header() +{ + /* variables utilitarias (iterar y/o buffer) */ + byte h, l; + /* identificador del paquete IP (incrementa con cada paquete) */ + static uint16 id; + /* reseteamos checksum */ + checksum = 0; + /* versión (4) y tamaño de cabecera (5 words de 4 bytes = 20 bytes) */ + netdev_send_byte(h = 0x45); + /* TOS (0xc0 = Internetwork Control, 0x00 = normal) */ + l = (ip_proto == IP_ICMP) ? 0xc0 : 0x00; + netdev_send_byte(l); + sum(WORD(h, l)); /* actualizamos checksum */ + /* escribimos tamaño del paquete */ + netdev_send_byte(h = 0x00); /* nunca vamos a mandar algo de más de 255 bytes */ + netdev_send_byte(ip_packet_len); + sum(WORD(h, ip_packet_len)); /* actualizamos checksum */ + /* identificación (sirve para reensamblar paquetes) */ + netdev_send_word(id); + sum(id); /* actualizamos checksum */ + /* pedimos que no se fragmente */ + netdev_send_byte(h = 0x40); /* Don't Fragment (DF) = 1 */ + netdev_send_byte(l = 0x00); /* offset de fragmento = 0 */ + sum(WORD(h, l)); /* actualizamos checksum */ + /* TTL de 64 saltos porque está de moda */ + netdev_send_byte(h = 0x40); + /* protocolo (sólo soportamos UDP e ICMP) */ + l = (ip_proto == IP_ICMP) ? ICMP : UDP; + netdev_send_byte(l); + sum(WORD(h, l)); /* actualizamos checksum */ + /* checksum: antes de poder escribir el checksum hay que terminar de + * calcularlo según las direcciones IP de origen y destino, así que eso + * hacemos */ + for (l = 0; l < IP_ADDR_SIZE; ++l) /* origen = local */ + if (l % 2) + sum(WORD(h, ip_addr_local[l])); + else + h = ip_addr_local[l]; + for (l = 0; l < IP_ADDR_SIZE; ++l) /* destino = remota */ + if (l % 2) + sum(WORD(h, ip_addr_remote[l])); + else + h = ip_addr_remote[l]; + /* ahora sí grabamos el checksum */ + netdev_send_word(~checksum); + /* ahora sí, continuamos poniendo las direcciones */ + /* ponemos como dirección IP de origen la nuestra */ + for (l = 0; l < IP_ADDR_SIZE; ++l) + netdev_send_byte(ip_addr_local[l]); + /* IP de destino, la remota */ + for (l = 0; l < IP_ADDR_SIZE; ++l) + netdev_send_byte(ip_addr_remote[l]); +} + diff --git a/src/c/ip.h b/src/ip.h similarity index 94% rename from src/c/ip.h rename to src/ip.h index ec3a892..0b3fff1 100644 --- a/src/c/ip.h +++ b/src/ip.h @@ -1,8 +1,9 @@ +// vim: set et sw=4 sts=4 : + #ifndef _IP_H_ #define _IP_H_ #include "types.h" -#include "net.h" /** @file * Paquete IP. @@ -34,6 +35,9 @@ /** Tamaño de dirección IP (en bytes) */ #define IP_ADDR_SIZE 4 +/** Tipos de paquete IP */ +typedef enum { IP_UDP, IP_ICMP } ip_proto_t; + /** Dirección IP nuestra */ extern byte ip_addr_local[IP_ADDR_SIZE]; @@ -44,7 +48,7 @@ extern byte ip_addr_remote[IP_ADDR_SIZE]; extern byte ip_packet_len; /** Indica si el paquete es ICMP (si no es UDP) */ -bool ip_proto_icmp; +extern ip_proto_t ip_proto; /** Lee la cabecera del paquete IP. * diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7cd2716 --- /dev/null +++ b/src/main.c @@ -0,0 +1,112 @@ +// vim: set et sw=4 sts=4 : + +#include "netdev.h" +#include "eth.h" +#include "ip.h" +#include "udp.h" + +#ifdef SDCC +static xdata at 0x0080 byte leds0; +static xdata at 0x00c0 byte leds1; +#else +static byte xdata leds0 _at_ 0x0080; +static byte xdata leds1 _at_ 0x00c0; +#endif + +#define leds(word) \ + do \ + { \ + leds0 = ~LOW(word); \ + leds1 = ~HIGH(word); \ + } \ + while (0) + + +void sleep(unsigned char times) +{ + unsigned int i; + unsigned char j; + for (i = 0; i < 0xffff; ++i) + for (j = 0; j < times; ++j); +} + +void main(void) +{ + // Apagamos todos los leds + leds(0x0000); + + // Inicializamos dispositivo de red + if (!netdev_init()) + { + leds(0xffff); + while(1); // Si falla init nos quedamos bobos + } + + // Inicializo IP + ip_addr_local[0] = 10; + ip_addr_local[1] = 10; + ip_addr_local[2] = 10; + ip_addr_local[3] = 100; + + // Inicializo puerto UDP + udp_port_local = 9000; + + while (1) // Forever + { + uint16 len = netdev_recv_start(); + if (!len) // no recibimos nada + { + netdev_recv_end(); + continue; // vamos de nuevo! + } + + // Parseamos cabecera ethernet + if (!eth_read_frame_header()) // No es un buen header + { + // Tenemos algo! + netdev_recv_end(); // Tiramos el paquete + continue; // Vamos de nuevo! + } + + // Vemos que protocolo transporta + switch (eth_proto) + { + case ETH_ARP: // FIXME, implementar ARP! + netdev_recv_end(); // Tiramos el paquete + continue; // Vamos de nuevo! + + case ETH_IP: + // Parseamos cabecera IP + if (!ip_read_packet_header()) // No es un buen header + { + netdev_recv_end(); // Tiramos el paquete + continue; // Vamos de nuevo! + } + + // Vemos que protocolo transporta + switch (ip_proto) + { + case IP_ICMP: // FIXME, implementar ICMP! + netdev_recv_end(); // Tiramos el paquete + continue; // Vamos de nuevo! + + case IP_UDP: + // Parseamos cabecera UDP + if (!udp_read_dgram_header()) // No es un buen header + { + netdev_recv_end(); // Tiramos el paquete + continue; // Vamos de nuevo! + } + + // TODO + // Nuestro protocolo, por ahora un simple echo! + for (len = 8; len < udp_dgram_len; len += 2) // 8 por la cabecera UDP + { + leds(netdev_recv_word()); + sleep(5); + } + } + } + } +} + diff --git a/src/netdev.h b/src/netdev.h new file mode 100644 index 0000000..635432b --- /dev/null +++ b/src/netdev.h @@ -0,0 +1,55 @@ +// vim: set et sw=4 sts=4 : + +#ifndef _NETDEV_H_ +#define _NETDEV_H_ + +#include "types.h" + +/** Inicializa dispositivo de red + * @return true si se inicializó correctamente, false si no + */ +bool netdev_init(); + +/** Comienza el envío de un nuevo frame + * @param len Tamaño del frame a enviar + */ +void netdev_send_start(); + +/** Escribe un byte al buffer de la placa de red para ser enviado + * @precond netdev_send_start() debe haber sido ejecutada + * @param b Byte a enviar + */ +void netdev_send_byte(byte b); + +/** Escribe un word al buffer de la placa de red para ser enviado + * @precond netdev_send_start() debe haber sido ejecutada + * @param w Word a enviar + */ +void netdev_send_word(uint16 w); + +/** Finaliza el envío del frame + * @precond netdev_send_start() debe haber sido ejecutada + */ +void netdev_send_end(); + +/** Comienza la lectura de un nuevo frame + * @return Cantidad de bytes del frame leído + */ +byte netdev_recv_start(); + +/** Lee un byte del buffer de la placa de red + * @precond netdev_recv_start() debe haber sido ejecutada + */ +byte netdev_recv_byte(); + +/** Lee un word del buffer de la placa de red + * @precond netdev_recv_start() debe haber sido ejecutada + */ +uint16 netdev_recv_word(); + +/** Finaliza la lectura del frame + * @precond netdev_recv_start() debe haber sido ejecutada + */ +void netdev_recv_end(); + +#endif // _NETDEV_H_ diff --git a/src/reg51.h b/src/reg51.h new file mode 100644 index 0000000..7b95f31 --- /dev/null +++ b/src/reg51.h @@ -0,0 +1,10 @@ +#ifndef _REG51_H_ +#define _REG51_H_ + +#ifdef SDCC +# include "reg51sdcc.h" +#else +# include "reg51keil.h" +#endif + +#endif /* _REG51_H_ */ diff --git a/src/startup.a51 b/src/startup.a51 new file mode 100644 index 0000000..d9a8780 --- /dev/null +++ b/src/startup.a51 @@ -0,0 +1,157 @@ +$NOMOD51 +;------------------------------------------------------------------------------ +; This file is part of the C51 Compiler package +; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc. +;------------------------------------------------------------------------------ +; STARTUP.A51: This code is executed after processor reset. +; +; To translate this file use A51 with the following invocation: +; +; A51 STARTUP.A51 +; +; To link the modified STARTUP.OBJ file to your application use the following +; BL51 invocation: +; +; BL51 , STARTUP.OBJ +; +;------------------------------------------------------------------------------ +; +; User-defined Power-On Initialization of Memory +; +; With the following EQU statements the initialization of memory +; at processor reset can be defined: +; +; ; the absolute start-address of IDATA memory is always 0 +IDATALEN EQU 80H ; the length of IDATA memory in bytes. +; +XDATASTART EQU 0H ; the absolute start-address of XDATA memory +XDATALEN EQU 400H ; the length of XDATA memory in bytes. +; +PDATASTART EQU 0H ; the absolute start-address of PDATA memory +PDATALEN EQU 0H ; the length of PDATA memory in bytes. +; +; Notes: The IDATA space overlaps physically the DATA and BIT areas of the +; 8051 CPU. At minimum the memory space occupied from the C51 +; run-time routines must be set to zero. +;------------------------------------------------------------------------------ +; +; Reentrant Stack Initilization +; +; The following EQU statements define the stack pointer for reentrant +; functions and initialized it: +; +; Stack Space for reentrant functions in the SMALL model. +IBPSTACK EQU 0 ; set to 1 if small reentrant is used. +IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1. +; +; Stack Space for reentrant functions in the LARGE model. +XBPSTACK EQU 0 ; set to 1 if large reentrant is used. +XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. +; +; Stack Space for reentrant functions in the COMPACT model. +PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. +PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. +; +;------------------------------------------------------------------------------ +; +; Page Definition for Using the Compact Model with 64 KByte xdata RAM +; +; The following EQU statements define the xdata page used for pdata +; variables. The EQU PPAGE must conform with the PPAGE control used +; in the linker invocation. +; +PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. +; +PPAGE EQU 0 ; define PPAGE number. +; +PPAGE_SFR DATA 0A0H ; SFR that supplies uppermost address byte +; (most 8051 variants use P2 as uppermost address byte) +; +;------------------------------------------------------------------------------ + +; Standard SFR Symbols +ACC DATA 0E0H +B DATA 0F0H +SP DATA 81H +DPL DATA 82H +DPH DATA 83H + + NAME ?C_STARTUP + + +?C_C51STARTUP SEGMENT CODE +?STACK SEGMENT IDATA + + RSEG ?STACK + DS 1 + + EXTRN CODE (?C_START) + PUBLIC ?C_STARTUP + + CSEG AT 0 +?C_STARTUP: LJMP STARTUP1 + + RSEG ?C_C51STARTUP + +STARTUP1: + +IF IDATALEN <> 0 + MOV R0,#IDATALEN - 1 + CLR A +IDATALOOP: MOV @R0,A + DJNZ R0,IDATALOOP +ENDIF + +IF XDATALEN <> 0 + MOV DPTR,#XDATASTART + MOV R7,#LOW (XDATALEN) + IF (LOW (XDATALEN)) <> 0 + MOV R6,#(HIGH (XDATALEN)) +1 + ELSE + MOV R6,#HIGH (XDATALEN) + ENDIF + CLR A +XDATALOOP: MOVX @DPTR,A + INC DPTR + DJNZ R7,XDATALOOP + DJNZ R6,XDATALOOP +ENDIF + +IF PPAGEENABLE <> 0 + MOV PPAGE_SFR,#PPAGE +ENDIF + +IF PDATALEN <> 0 + MOV R0,#LOW (PDATASTART) + MOV R7,#LOW (PDATALEN) + CLR A +PDATALOOP: MOVX @R0,A + INC R0 + DJNZ R7,PDATALOOP +ENDIF + +IF IBPSTACK <> 0 +EXTRN DATA (?C_IBP) + + MOV ?C_IBP,#LOW IBPSTACKTOP +ENDIF + +IF XBPSTACK <> 0 +EXTRN DATA (?C_XBP) + + MOV ?C_XBP,#HIGH XBPSTACKTOP + MOV ?C_XBP+1,#LOW XBPSTACKTOP +ENDIF + +IF PBPSTACK <> 0 +EXTRN DATA (?C_PBP) + MOV ?C_PBP,#LOW PBPSTACKTOP +ENDIF + + MOV SP,#?STACK-1 +; This code is required if you use L51_BANK.A51 with Banking Mode 4 +; EXTRN CODE (?B_SWITCH0) +; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 + LJMP ?C_START + + END diff --git a/src/c/types.h b/src/types.h similarity index 57% rename from src/c/types.h rename to src/types.h index 52b07d1..3bd0dc6 100644 --- a/src/c/types.h +++ b/src/types.h @@ -1,38 +1,21 @@ #ifndef _TYPES_H_ #define _TYPES_H_ -/** valores posibles de un booleano */ -enum { false = 0, true = 1 }; - -/** convierte 2 bytes (high, low) en un word */ -#define WORD(high, low) ((high << 8) + low) - -/** convierte un word en 2 bytes */ -#define UNPACK(word, high, low) (high = (word >> 8), low = word) - -/** obtiene parte alta de un word */ -#define HIGH(word) (word >> 8) - -/** obtiene parte baja de un word */ -#define LOW(word) (word) - -#if 1 /* i386 */ +#if 0 /* i386 */ +#include /** booleano */ typedef unsigned char bool; /** entero sin signo de 8 bits */ -typedef unsigned char byte; +typedef uint8_t byte; /** entero sin signo de 16 bits */ -typedef unsigned short uint16; +typedef uint16_t uint16; #endif -#if 0 /* 8051 */ - -/** booleano */ -typedef unsigned char bool; +#if 1 /* 8051 */ /** entero sin signo de 8 bits */ typedef unsigned char byte; @@ -40,6 +23,30 @@ typedef unsigned char byte; /** entero sin signo de 16 bits */ typedef unsigned int uint16; +#ifdef SDCC +/** booleano */ +typedef byte bool; +#else +/** booleano */ +typedef bit bool; +#endif + #endif +/** valores posibles de un booleano */ +enum { false = 0, true = 1 }; + +/** convierte 2 bytes (high, low) en un word */ +#define WORD(high, low) ((uint16)((uint16)(high << 8) + (uint16)low)) + +/** convierte un word en 2 bytes */ +#define UNPACK(word, high, low) (high = (byte)(word >> 8), \ + low = (byte)word & 0xFF) + +/** obtiene parte alta de un word */ +#define HIGH(word) ((byte)(word >> 8)) + +/** obtiene parte baja de un word */ +#define LOW(word) ((byte)(word & 0xFF)) + #endif /* _TYPES_H_ */ diff --git a/src/udp.c b/src/udp.c new file mode 100644 index 0000000..9da1b25 --- /dev/null +++ b/src/udp.c @@ -0,0 +1,118 @@ +// vim: set et sw=4 sts=4 : + +#include "netdev.h" +#include "ip.h" +#include "udp.h" + +uint16 udp_port_local; + +uint16 udp_port_remote; + +byte udp_dgram_len; + +/* para calcular checksum */ +static uint16 checksum; + +/* agrega un word al checksum calculado */ +static void sum(uint16 w) +{ + checksum += w; + if (checksum < w) /* corrección de carry (hubo OV) */ + ++checksum; +} + +bool udp_read_dgram_header() +{ + uint16 p; + /* reseteamos checksum */ + checksum = 0; + /* el UDP tiene un checksum que incluye parte de la cabecera IP */ + /* ip de origen */ + sum(WORD(ip_addr_remote[0], ip_addr_remote[1])); + sum(WORD(ip_addr_remote[2], ip_addr_remote[3])); + /* ip de destino */ + sum(WORD(ip_addr_local[0], ip_addr_local[1])); + sum(WORD(ip_addr_local[2], ip_addr_local[3])); + /* protocolo expresado en 16 bits (0x11 es UDP) */ + sum(0x0011); + /* tamaño del paquete UDP (sin las cabeceras que son 20 bytes) */ + sum(ip_packet_len - 20); + /* de ahora en más todos los datos del checksum corresponden a UDP */ + /* puerto origen (remoto) */ + udp_port_remote = netdev_recv_word(); + /* agregamos puerto de origen al checksum */ + sum(udp_port_remote); + /* sólo aceptamos datagramas a nuestro puerto */ + p = netdev_recv_word(); + if (p != udp_port_local) + return false; /* drop */ + /* agregamos puerto de destino al checksum */ + sum(udp_port_local); + /* tamaño del datagrama */ + if (netdev_recv_byte()) /* no soportamos más de 255 bytes */ + return false; /* drop */ + udp_dgram_len = netdev_recv_byte(); + /* agregamos tamaño al checksum */ + sum(udp_dgram_len); + /* agregamos checksum al checksum */ + sum(netdev_recv_word()); + /* falta agregar el cuerpo del mensaje para verificar la suma + * esto debe hacerlo el protocolo que sigue para poder seguir obteniendo + * los datos de la placa de red byte a byte */ + return true; +} + +uint16 udp_read_word() +{ + uint16 w = netdev_recv_word(); + sum(w); + return w; +} + +bool udp_checksum_ok() +{ + return (uint16)~checksum; +} + +void udp_write_dgram_header() +{ + /* reseteamos checksum */ + checksum = 0; + /* el UDP tiene un checksum que incluye parte de la cabecera IP */ + /* ip de origen */ + sum(WORD(ip_addr_remote[0], ip_addr_remote[1])); + sum(WORD(ip_addr_remote[2], ip_addr_remote[3])); + /* ip de destino */ + sum(WORD(ip_addr_local[0], ip_addr_local[1])); + sum(WORD(ip_addr_local[2], ip_addr_local[3])); + /* protocolo expresado en 16 bits (0x11 es UDP) */ + sum(0x0011); + /* tamaño del paquete UDP (IP sin las cabeceras, que son 20 bytes) */ + sum(ip_packet_len - 20); // FIXME + /* puerto origen */ + netdev_send_word(udp_port_local); + sum(udp_port_local); + /* puerto destino */ + netdev_send_word(udp_port_remote); + sum(udp_port_remote); + /* tamaño del datagrama */ + netdev_send_byte(0x00); /* parte alta en 0 porque no soportamos más de 255 */ + netdev_send_byte(udp_dgram_len); + sum(WORD(0x00, udp_dgram_len)); + /* indicamos que no se usa checksum */ + netdev_send_word(0x0000); + sum(0x0000); +} + +void udp_write_word(uint16 w) +{ + sum(w); + netdev_send_word(w); +} + +void udp_write_checksum() +{ + // XXX TODO FIXME + return; +} + diff --git a/src/c/udp.h b/src/udp.h similarity index 80% rename from src/c/udp.h rename to src/udp.h index 9530a13..e9092f7 100644 --- a/src/c/udp.h +++ b/src/udp.h @@ -1,8 +1,9 @@ +// vim: set et sw=4 sts=4 : + #ifndef _UDP_H_ #define _UDP_H_ #include "types.h" -#include "net.h" /** @file * Datagrama UDP. @@ -27,7 +28,8 @@ * es 8. * * El checksum se calcula utilizando algunos datos de la capa inferior e incluye - * a los datos. Entonces es calculado sobre el siguiente paquete 'virtual': + * a los datos (completando con 0 al final si no es múltiplo de 2). Entonces es + * calculado sobre el siguiente paquete 'virtual': * *
  *  0      7 8     15 16    23 24    31  
@@ -45,7 +47,7 @@
  * |     Length      |    Checksum     | 
  * +--------+--------+--------+--------+ 
  * |                                     
- * |          data octets ...            
+ * |    data octets ... (padding)
  * +---------------- ...                 
  * 
*/ @@ -67,6 +69,12 @@ extern byte udp_dgram_len; */ bool udp_read_dgram_header(); +/** Recibe un word del payload UDP chequeando el checksum. */ +uint16 udp_read_word(); + +/* Indica si el checksum calculado está ok */ +bool udp_checksum_ok(); + /** Escribe la cabecera del datagrama UDP. * * Pone como puerto destino a udp_port_remote, como origen udp_port_local y como @@ -74,4 +82,10 @@ bool udp_read_dgram_header(); */ void udp_write_dgram_header(); +/** Escribe un word al payload UDP chequeando el checksum. */ +void udp_write_word(uint16 w); + +/* Escribe el checksum calculado al frame a enviar */ +void udp_write_checksum(); + #endif /* _UDP_H_ */